1use crate::error::RvfError;
8
9pub const REFCOUNT_MAGIC: u32 = 0x5256_5243;
11
12#[derive(Clone, Copy, Debug)]
17#[repr(C)]
18pub struct RefcountHeader {
19 pub magic: u32,
21 pub version: u16,
23 pub refcount_width: u8,
25 pub _pad: u8,
27 pub cluster_count: u32,
29 pub max_refcount: u32,
31 pub array_offset: u64,
33 pub snapshot_epoch: u32,
35 pub _reserved: u32,
37}
38
39const _: () = assert!(core::mem::size_of::<RefcountHeader>() == 32);
41
42impl RefcountHeader {
43 pub fn to_bytes(&self) -> [u8; 32] {
45 let mut buf = [0u8; 32];
46 buf[0x00..0x04].copy_from_slice(&self.magic.to_le_bytes());
47 buf[0x04..0x06].copy_from_slice(&self.version.to_le_bytes());
48 buf[0x06] = self.refcount_width;
49 buf[0x07] = self._pad;
50 buf[0x08..0x0C].copy_from_slice(&self.cluster_count.to_le_bytes());
51 buf[0x0C..0x10].copy_from_slice(&self.max_refcount.to_le_bytes());
52 buf[0x10..0x18].copy_from_slice(&self.array_offset.to_le_bytes());
53 buf[0x18..0x1C].copy_from_slice(&self.snapshot_epoch.to_le_bytes());
54 buf[0x1C..0x20].copy_from_slice(&self._reserved.to_le_bytes());
55 buf
56 }
57
58 pub fn from_bytes(data: &[u8; 32]) -> Result<Self, RvfError> {
60 let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
61 if magic != REFCOUNT_MAGIC {
62 return Err(RvfError::BadMagic {
63 expected: REFCOUNT_MAGIC,
64 got: magic,
65 });
66 }
67
68 let refcount_width = data[0x06];
69 let pad = data[0x07];
70 let reserved = u32::from_le_bytes([data[0x1C], data[0x1D], data[0x1E], data[0x1F]]);
71
72 if refcount_width != 1 && refcount_width != 2 && refcount_width != 4 {
74 return Err(RvfError::InvalidEnumValue {
75 type_name: "RefcountHeader::refcount_width",
76 value: refcount_width as u64,
77 });
78 }
79
80 if pad != 0 {
82 return Err(RvfError::InvalidEnumValue {
83 type_name: "RefcountHeader::_pad",
84 value: pad as u64,
85 });
86 }
87 if reserved != 0 {
88 return Err(RvfError::InvalidEnumValue {
89 type_name: "RefcountHeader::_reserved",
90 value: reserved as u64,
91 });
92 }
93
94 Ok(Self {
95 magic,
96 version: u16::from_le_bytes([data[0x04], data[0x05]]),
97 refcount_width,
98 _pad: pad,
99 cluster_count: u32::from_le_bytes([data[0x08], data[0x09], data[0x0A], data[0x0B]]),
100 max_refcount: u32::from_le_bytes([data[0x0C], data[0x0D], data[0x0E], data[0x0F]]),
101 array_offset: u64::from_le_bytes([
102 data[0x10], data[0x11], data[0x12], data[0x13],
103 data[0x14], data[0x15], data[0x16], data[0x17],
104 ]),
105 snapshot_epoch: u32::from_le_bytes([data[0x18], data[0x19], data[0x1A], data[0x1B]]),
106 _reserved: reserved,
107 })
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 fn sample_header() -> RefcountHeader {
116 RefcountHeader {
117 magic: REFCOUNT_MAGIC,
118 version: 1,
119 refcount_width: 2,
120 _pad: 0,
121 cluster_count: 1024,
122 max_refcount: 65535,
123 array_offset: 64,
124 snapshot_epoch: 0,
125 _reserved: 0,
126 }
127 }
128
129 #[test]
130 fn header_size_is_32() {
131 assert_eq!(core::mem::size_of::<RefcountHeader>(), 32);
132 }
133
134 #[test]
135 fn magic_bytes_match_ascii() {
136 let bytes_be = REFCOUNT_MAGIC.to_be_bytes();
137 assert_eq!(&bytes_be, b"RVRC");
138 }
139
140 #[test]
141 fn round_trip_serialization() {
142 let original = sample_header();
143 let bytes = original.to_bytes();
144 let decoded = RefcountHeader::from_bytes(&bytes).expect("from_bytes should succeed");
145
146 assert_eq!(decoded.magic, REFCOUNT_MAGIC);
147 assert_eq!(decoded.version, 1);
148 assert_eq!(decoded.refcount_width, 2);
149 assert_eq!(decoded._pad, 0);
150 assert_eq!(decoded.cluster_count, 1024);
151 assert_eq!(decoded.max_refcount, 65535);
152 assert_eq!(decoded.array_offset, 64);
153 assert_eq!(decoded.snapshot_epoch, 0);
154 assert_eq!(decoded._reserved, 0);
155 }
156
157 #[test]
158 fn bad_magic_returns_error() {
159 let mut bytes = sample_header().to_bytes();
160 bytes[0] = 0x00; let err = RefcountHeader::from_bytes(&bytes).unwrap_err();
162 match err {
163 RvfError::BadMagic { expected, .. } => assert_eq!(expected, REFCOUNT_MAGIC),
164 other => panic!("expected BadMagic, got {other:?}"),
165 }
166 }
167
168 #[test]
169 fn field_offsets() {
170 let h = sample_header();
171 let base = &h as *const _ as usize;
172
173 assert_eq!(&h.magic as *const _ as usize - base, 0x00);
174 assert_eq!(&h.version as *const _ as usize - base, 0x04);
175 assert_eq!(&h.refcount_width as *const _ as usize - base, 0x06);
176 assert_eq!(&h._pad as *const _ as usize - base, 0x07);
177 assert_eq!(&h.cluster_count as *const _ as usize - base, 0x08);
178 assert_eq!(&h.max_refcount as *const _ as usize - base, 0x0C);
179 assert_eq!(&h.array_offset as *const _ as usize - base, 0x10);
180 assert_eq!(&h.snapshot_epoch as *const _ as usize - base, 0x18);
181 assert_eq!(&h._reserved as *const _ as usize - base, 0x1C);
182 }
183
184 #[test]
185 fn frozen_snapshot_epoch() {
186 let mut h = sample_header();
187 h.snapshot_epoch = 42;
188 let bytes = h.to_bytes();
189 let decoded = RefcountHeader::from_bytes(&bytes).unwrap();
190 assert_eq!(decoded.snapshot_epoch, 42);
191 }
192}