1use crate::coding::{bits_needed, checked_count, checked_decode_count, pack_bits, unpack_bits};
27use crate::error::{EncodingError, Result};
28
29const HEADER_SIZE: usize = 13;
31
32#[derive(Debug, Clone, Copy)]
37pub struct ForEncoder;
38
39#[derive(Debug, Clone, Copy)]
41pub struct ForDecoder;
42
43impl ForEncoder {
44 pub fn encode_i64(values: &[i64]) -> Result<Vec<u8>> {
50 if values.is_empty() {
51 return Err(EncodingError::EmptyInput {
52 context: "FOR encoder (i64)",
53 });
54 }
55
56 let count = checked_count(values.len())?;
57
58 let reference = values.iter().copied().min().unwrap_or(0);
60
61 let offsets: Vec<u64> = values
66 .iter()
67 .map(|&v| v.wrapping_sub(reference) as u64)
68 .collect();
69 let max_offset = offsets.iter().copied().max().unwrap_or(0);
70 let bit_width = bits_needed(max_offset);
71
72 let data_bits = offsets.len() as u64 * u64::from(bit_width);
74 let data_bytes = data_bits.div_ceil(8) as usize;
75 let mut buf = Vec::with_capacity(HEADER_SIZE + data_bytes);
76
77 buf.extend_from_slice(&count.to_le_bytes());
79 buf.extend_from_slice(&reference.to_le_bytes());
80 buf.push(bit_width);
81
82 if bit_width == 0 {
83 return Ok(buf);
85 }
86
87 pack_bits(&offsets, bit_width, &mut buf);
89
90 Ok(buf)
91 }
92
93 pub fn encode_u64(values: &[u64]) -> Result<Vec<u8>> {
102 if values.is_empty() {
103 return Err(EncodingError::EmptyInput {
104 context: "FOR encoder (u64)",
105 });
106 }
107
108 let count = checked_count(values.len())?;
109
110 let reference = values.iter().copied().min().unwrap_or(0);
111
112 let offsets: Vec<u64> = values.iter().map(|&v| v - reference).collect();
113 let max_offset = offsets.iter().copied().max().unwrap_or(0);
114 let bit_width = bits_needed(max_offset);
115
116 let data_bits = offsets.len() as u64 * u64::from(bit_width);
117 let data_bytes = data_bits.div_ceil(8) as usize;
118 let mut buf = Vec::with_capacity(HEADER_SIZE + data_bytes);
119
120 buf.extend_from_slice(&count.to_le_bytes());
122 buf.extend_from_slice(&(reference as i64).to_le_bytes());
123 buf.push(bit_width);
124
125 if bit_width == 0 {
126 return Ok(buf);
127 }
128
129 pack_bits(&offsets, bit_width, &mut buf);
130
131 Ok(buf)
132 }
133}
134
135impl ForDecoder {
136 pub fn decode_i64(data: &[u8]) -> Result<Vec<i64>> {
142 if data.len() < HEADER_SIZE {
143 return Err(EncodingError::CorruptData {
144 detail: "FOR data too short for header".to_string(),
145 });
146 }
147
148 let count = checked_decode_count(
149 u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize,
150 "FOR",
151 )?;
152 let reference = i64::from_le_bytes([
153 data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11],
154 ]);
155 let bit_width = data[12];
156
157 if bit_width > 64 {
158 return Err(EncodingError::CorruptData {
159 detail: format!("invalid FOR bit_width {bit_width}, max is 64"),
160 });
161 }
162
163 if count == 0 {
164 return Ok(Vec::new());
165 }
166
167 if bit_width == 0 {
169 return Ok(vec![reference; count]);
170 }
171
172 let packed_data = &data[HEADER_SIZE..];
174 let offsets = unpack_bits(packed_data, count, bit_width)?;
175
176 let mut values = Vec::new();
178 crate::simd::batch_add_scalar_i64(reference, &offsets, &mut values);
179
180 Ok(values)
181 }
182
183 pub fn decode_u64(data: &[u8]) -> Result<Vec<u64>> {
191 if data.len() < HEADER_SIZE {
192 return Err(EncodingError::CorruptData {
193 detail: "FOR data too short for header".to_string(),
194 });
195 }
196
197 let count = checked_decode_count(
198 u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize,
199 "FOR",
200 )?;
201 let reference_i64 = i64::from_le_bytes([
202 data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11],
203 ]);
204 let reference = reference_i64 as u64;
205 let bit_width = data[12];
206
207 if bit_width > 64 {
208 return Err(EncodingError::CorruptData {
209 detail: format!("invalid FOR bit_width {bit_width}, max is 64"),
210 });
211 }
212
213 if count == 0 {
214 return Ok(Vec::new());
215 }
216
217 if bit_width == 0 {
218 return Ok(vec![reference; count]);
219 }
220
221 let packed_data = &data[HEADER_SIZE..];
222 let offsets = unpack_bits(packed_data, count, bit_width)?;
223
224 let mut values = Vec::new();
226 crate::simd::batch_add_scalar_u64(reference, &offsets, &mut values);
227
228 Ok(values)
229 }
230}
231
232#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn roundtrip_monotonic() {
242 let values: Vec<i64> = (100..200).collect();
243 let encoded = ForEncoder::encode_i64(&values).unwrap();
244 let decoded = ForDecoder::decode_i64(&encoded).unwrap();
245 assert_eq!(values, decoded);
246 }
247
248 #[test]
249 fn roundtrip_constant() {
250 let values = vec![42_i64; 1000];
251 let encoded = ForEncoder::encode_i64(&values).unwrap();
252 assert_eq!(encoded.len(), 13);
254 let decoded = ForDecoder::decode_i64(&encoded).unwrap();
255 assert_eq!(values, decoded);
256 }
257
258 #[test]
259 fn roundtrip_narrow_range() {
260 let values = vec![200, 201, 204, 301, 302, 400, 404, 500, 502, 503];
262 let encoded = ForEncoder::encode_i64(&values).unwrap();
263 let decoded = ForDecoder::decode_i64(&encoded).unwrap();
264 assert_eq!(values, decoded);
265
266 assert!(encoded.len() < 80);
270 }
271
272 #[test]
273 fn roundtrip_single_value() {
274 let values = vec![i64::MAX];
275 let encoded = ForEncoder::encode_i64(&values).unwrap();
276 let decoded = ForDecoder::decode_i64(&encoded).unwrap();
277 assert_eq!(values, decoded);
278 }
279
280 #[test]
281 fn roundtrip_negative_values() {
282 let values = vec![-100, -50, -10, -5, -1, 0, 5, 10, 50, 100];
283 let encoded = ForEncoder::encode_i64(&values).unwrap();
284 let decoded = ForDecoder::decode_i64(&encoded).unwrap();
285 assert_eq!(values, decoded);
286 }
287
288 #[test]
289 fn roundtrip_u64() {
290 let values: Vec<u64> = (1000..1100).collect();
291 let encoded = ForEncoder::encode_u64(&values).unwrap();
292 let decoded = ForDecoder::decode_u64(&encoded).unwrap();
293 assert_eq!(values, decoded);
294 }
295
296 #[test]
297 fn empty_input_error() {
298 assert!(ForEncoder::encode_i64(&[]).is_err());
299 assert!(ForEncoder::encode_u64(&[]).is_err());
300 }
301
302 #[test]
303 fn corrupt_data_too_short() {
304 assert!(ForDecoder::decode_i64(&[0; 5]).is_err());
305 }
306
307 #[test]
308 fn corrupt_bit_width_too_large() {
309 let mut data = vec![0u8; 13];
310 data[0] = 1;
312 data[12] = 65;
314 assert!(ForDecoder::decode_i64(&data).is_err());
315 }
316
317 #[test]
318 fn compression_vs_plain_narrow_range() {
319 let values: Vec<i64> = (0..1000).map(|i| 1000 + (i % 16)).collect();
321 let raw_size = values.len() * 8; let encoded = ForEncoder::encode_i64(&values).unwrap();
323 assert!(
325 encoded.len() < raw_size / 10,
326 "FOR should achieve >10× for 4-bit range"
327 );
328 let decoded = ForDecoder::decode_i64(&encoded).unwrap();
329 assert_eq!(values, decoded);
330 }
331
332 #[test]
333 fn compression_worse_for_wide_range() {
334 let values = vec![i64::MIN, 0, i64::MAX];
336 let encoded = ForEncoder::encode_i64(&values).unwrap();
337 let decoded = ForDecoder::decode_i64(&encoded).unwrap();
342 assert_eq!(values, decoded);
343 }
344
345 #[test]
346 fn roundtrip_two_values() {
347 let values = vec![10_i64, 20];
348 let encoded = ForEncoder::encode_i64(&values).unwrap();
349 let decoded = ForDecoder::decode_i64(&encoded).unwrap();
350 assert_eq!(values, decoded);
351 }
352
353 #[test]
354 fn roundtrip_all_same_negative() {
355 let values = vec![-999_i64; 500];
356 let encoded = ForEncoder::encode_i64(&values).unwrap();
357 assert_eq!(encoded.len(), 13); let decoded = ForDecoder::decode_i64(&encoded).unwrap();
359 assert_eq!(values, decoded);
360 }
361
362 #[test]
363 fn u64_sign_boundary_narrow_range() {
364 let mid = (1u64 << 63) - 1; let values = vec![mid, mid + 1, mid + 2];
369 let encoded = ForEncoder::encode_u64(&values).unwrap();
370 let decoded = ForDecoder::decode_u64(&encoded).unwrap();
371 assert_eq!(values, decoded);
372
373 assert!(
375 encoded.len() <= 14,
376 "expected ≤14 bytes, got {}",
377 encoded.len()
378 );
379 }
380
381 #[test]
382 fn u64_large_values() {
383 let values = vec![u64::MAX - 10, u64::MAX - 5, u64::MAX];
384 let encoded = ForEncoder::encode_u64(&values).unwrap();
385 let decoded = ForDecoder::decode_u64(&encoded).unwrap();
386 assert_eq!(values, decoded);
387 }
388}
389
390#[cfg(test)]
391mod proptests {
392 use super::*;
393 use proptest::prelude::*;
394
395 proptest! {
396 #[test]
397 fn roundtrip_i64(values in proptest::collection::vec(any::<i64>(), 1..500)) {
398 let encoded = ForEncoder::encode_i64(&values).unwrap();
399 let decoded = ForDecoder::decode_i64(&encoded).unwrap();
400 prop_assert_eq!(&values, &decoded);
401 }
402
403 #[test]
404 fn roundtrip_u64(values in proptest::collection::vec(any::<u64>(), 1..500)) {
405 let encoded = ForEncoder::encode_u64(&values).unwrap();
406 let decoded = ForDecoder::decode_u64(&encoded).unwrap();
407 prop_assert_eq!(&values, &decoded);
408 }
409 }
410}