1use crate::bytes::{le_u32, le_u64};
19use crate::error::UrnaError;
20
21pub const INTPACK_BLOCK: usize = 128;
23const HEADER: usize = 8; #[inline]
27fn bit_width(range: u64) -> u8 {
28 (64 - range.leading_zeros()) as u8
29}
30
31#[inline]
32fn mask(width: u8) -> u64 {
33 if width >= 64 {
34 u64::MAX
35 } else {
36 (1u64 << width) - 1
37 }
38}
39
40fn malformed(reason: impl Into<String>) -> UrnaError {
41 UrnaError::MalformedSectionPayload {
42 section_id: 0,
43 reason: reason.into(),
44 }
45}
46
47pub fn pack_u64s(values: &[u64]) -> Vec<u8> {
50 let count = values.len();
51 let n_blocks = count.div_ceil(INTPACK_BLOCK);
52 let blocks_start = HEADER + n_blocks * 4;
53 let mut dir: Vec<u32> = Vec::with_capacity(n_blocks);
54 let mut blocks: Vec<u8> = Vec::new();
55 for chunk in values.chunks(INTPACK_BLOCK) {
56 dir.push((blocks_start + blocks.len()) as u32);
57 let (min, max) = chunk
59 .iter()
60 .fold((u64::MAX, 0u64), |(lo, hi), &v| (lo.min(v), hi.max(v)));
61 let width = bit_width(max - min);
62 blocks.extend_from_slice(&min.to_le_bytes());
63 blocks.push(width);
64 pack_block(&mut blocks, chunk, min, width);
65 }
66 let mut out = Vec::with_capacity(blocks_start + blocks.len());
67 out.extend_from_slice(&(count as u32).to_le_bytes());
68 out.extend_from_slice(&(n_blocks as u32).to_le_bytes());
69 for off in &dir {
70 out.extend_from_slice(&off.to_le_bytes());
71 }
72 out.extend_from_slice(&blocks);
73 out
74}
75
76fn pack_block(out: &mut Vec<u8>, chunk: &[u64], min: u64, width: u8) {
77 if width == 0 {
78 return;
79 }
80 let mut acc: u128 = 0;
81 let mut nbits: u32 = 0;
82 for &v in chunk {
83 acc |= ((v - min) as u128 & mask(width) as u128) << nbits;
84 nbits += width as u32;
85 while nbits >= 8 {
86 out.push((acc & 0xff) as u8);
87 acc >>= 8;
88 nbits -= 8;
89 }
90 }
91 if nbits > 0 {
92 out.push((acc & 0xff) as u8);
93 }
94}
95
96#[inline]
97fn read_u32(bytes: &[u8], pos: usize) -> Result<u32, UrnaError> {
98 let end = pos + 4;
99 if end > bytes.len() {
100 return Err(malformed("intpack: truncated u32"));
101 }
102 le_u32(&bytes[pos..end])
103}
104
105#[inline]
106fn read_u64(bytes: &[u8], pos: usize) -> Result<u64, UrnaError> {
107 let end = pos + 8;
108 if end > bytes.len() {
109 return Err(malformed("intpack: truncated u64"));
110 }
111 le_u64(&bytes[pos..end])
112}
113
114#[inline]
117fn block_body_len(block_len: usize, width: u8) -> usize {
118 (block_len * width as usize).div_ceil(8)
119}
120
121fn extract(body: &[u8], idx: usize, width: u8) -> u64 {
124 if width == 0 {
125 return 0;
126 }
127 let bit = idx * width as usize;
128 let mut acc: u128 = 0;
129 let first = bit / 8;
130 let last = (bit + width as usize - 1) / 8;
131 for (k, &b) in body[first..=last].iter().enumerate() {
132 acc |= (b as u128) << (k * 8);
133 }
134 ((acc >> (bit % 8)) as u64) & mask(width)
135}
136
137pub fn unpack_u64s(bytes: &[u8]) -> Result<Vec<u64>, UrnaError> {
139 let reader = IntpackReader::parse(bytes)?;
140 let mut out = Vec::with_capacity(reader.len().min(1 << 20));
144 for b in 0..reader.n_blocks {
145 let (min, width, body, block_len) = reader.block(b)?;
146 for i in 0..block_len {
147 out.push(min.wrapping_add(extract(body, i, width)));
150 }
151 }
152 Ok(out)
153}
154
155pub struct IntpackReader<'a> {
159 bytes: &'a [u8],
160 count: usize,
161 n_blocks: usize,
162}
163
164impl<'a> IntpackReader<'a> {
165 pub fn parse(bytes: &'a [u8]) -> Result<Self, UrnaError> {
166 let count = read_u32(bytes, 0)? as usize;
167 let n_blocks = read_u32(bytes, 4)? as usize;
168 if n_blocks != count.div_ceil(INTPACK_BLOCK) {
169 return Err(malformed("intpack: block count inconsistent with count"));
170 }
171 if HEADER + n_blocks * 4 > bytes.len() {
173 return Err(malformed("intpack: truncated directory"));
174 }
175 Ok(Self {
176 bytes,
177 count,
178 n_blocks,
179 })
180 }
181
182 #[inline]
183 pub fn len(&self) -> usize {
184 self.count
185 }
186
187 #[inline]
188 pub fn is_empty(&self) -> bool {
189 self.count == 0
190 }
191
192 fn block(&self, b: usize) -> Result<(u64, u8, &'a [u8], usize), UrnaError> {
194 let off = read_u32(self.bytes, HEADER + b * 4)? as usize;
195 let min = read_u64(self.bytes, off)?;
196 let width_pos = off + 8;
197 if width_pos >= self.bytes.len() {
198 return Err(malformed("intpack: truncated block header"));
199 }
200 let width = self.bytes[width_pos];
201 if width > 64 {
202 return Err(malformed("intpack: block width out of range"));
203 }
204 let block_len = (self.count - b * INTPACK_BLOCK).min(INTPACK_BLOCK);
205 let body_start = width_pos + 1;
206 let body_end = body_start + block_body_len(block_len, width);
207 if body_end > self.bytes.len() {
208 return Err(malformed("intpack: truncated block body"));
209 }
210 Ok((min, width, &self.bytes[body_start..body_end], block_len))
211 }
212
213 pub fn get(&self, i: usize) -> Result<u64, UrnaError> {
216 if i >= self.count {
217 return Err(malformed("intpack: index out of range"));
218 }
219 let (min, width, body, _) = self.block(i / INTPACK_BLOCK)?;
220 Ok(min.wrapping_add(extract(body, i % INTPACK_BLOCK, width)))
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 fn roundtrip(values: &[u64]) {
229 let packed = pack_u64s(values);
230 let back = unpack_u64s(&packed).unwrap();
231 assert_eq!(back, values, "unpack_u64s mismatch");
232 let reader = IntpackReader::parse(&packed).unwrap();
233 assert_eq!(reader.len(), values.len());
234 for (i, &v) in values.iter().enumerate() {
235 assert_eq!(reader.get(i).unwrap(), v, "get({}) mismatch", i);
236 }
237 assert!(reader.get(values.len()).is_err(), "oob index must error");
238 }
239
240 #[test]
241 fn empty_roundtrips() {
242 roundtrip(&[]);
243 }
244
245 #[test]
246 fn single_block_widths() {
247 roundtrip(&[0]);
248 roundtrip(&[7, 7, 7, 7]); roundtrip(&[0, 1, 2, 3, 4, 5]);
250 roundtrip(&[5, 1, 9, 2, 30724, 0]); }
252
253 #[test]
254 fn crosses_block_boundary() {
255 let v: Vec<u64> = (0..300).map(|i| (i * 7) % 31).collect();
256 roundtrip(&v);
257 }
258
259 #[test]
260 fn wide_values_and_for_offset() {
261 roundtrip(&[1_000_000, 1_000_001, 1_000_005, 1_000_002]);
262 roundtrip(&[u64::MAX, 0, u64::MAX / 2]);
263 }
264
265 #[test]
266 fn tampered_min_does_not_panic() {
267 let mut packed = pack_u64s(&[1, 2, 3, 200]);
270 for byte in packed.iter_mut().skip(12).take(8) {
272 *byte = 0xFF;
273 }
274 let _ = unpack_u64s(&packed); if let Ok(r) = IntpackReader::parse(&packed) {
276 for i in 0..r.len() {
277 let _ = r.get(i);
278 }
279 }
280 }
281
282 #[test]
283 fn truncated_inputs_error_never_panic() {
284 let packed = pack_u64s(&[1, 2, 3, 4, 5]);
285 for cut in 0..packed.len() {
286 let _ = unpack_u64s(&packed[..cut]);
287 if let Ok(r) = IntpackReader::parse(&packed[..cut]) {
288 let _ = r.get(0);
289 let _ = r.get(r.len().saturating_sub(1));
290 }
291 }
292 let mut evil = Vec::new();
294 evil.extend_from_slice(&1_000_000u32.to_le_bytes());
295 evil.extend_from_slice(&7813u32.to_le_bytes());
296 assert!(IntpackReader::parse(&evil).is_err());
297 }
298}