Encode a single block (up to 1024 values).
(values: &[i64], out: &mut Vec<u8>)
| 10 | |
| 11 | /// Encode a single block (up to 1024 values). |
| 12 | pub(super) fn encode_block(values: &[i64], out: &mut Vec<u8>) { |
| 13 | let count = values.len() as u16; |
| 14 | |
| 15 | // Find min/max for FOR. |
| 16 | let mut min_val = values[0]; |
| 17 | let mut max_val = values[0]; |
| 18 | for &v in &values[1..] { |
| 19 | if v < min_val { |
| 20 | min_val = v; |
| 21 | } |
| 22 | if v > max_val { |
| 23 | max_val = v; |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | // Compute residuals and bit width. |
| 28 | let range = (max_val as u128).wrapping_sub(min_val as u128) as u64; |
| 29 | let bit_width = if range == 0 { |
| 30 | 0u8 |
| 31 | } else { |
| 32 | 64 - range.leading_zeros() as u8 |
| 33 | }; |
| 34 | |
| 35 | // Block header. |
| 36 | out.extend_from_slice(&count.to_le_bytes()); |
| 37 | out.push(bit_width); |
| 38 | out.extend_from_slice(&min_val.to_le_bytes()); |
| 39 | |
| 40 | if bit_width == 0 { |
| 41 | // All values identical — no packed data needed. |
| 42 | return; |
| 43 | } |
| 44 | |
| 45 | // Bit-pack residuals. |
| 46 | // This loop is structured for auto-vectorization: simple operations on |
| 47 | // contiguous arrays, no branches in the inner loop, predictable access. |
| 48 | let packed_bytes = (count as usize * bit_width as usize).div_ceil(8); |
| 49 | let pack_start = out.len(); |
| 50 | out.resize(pack_start + packed_bytes, 0); |
| 51 | let packed = &mut out[pack_start..]; |
| 52 | |
| 53 | let bw = bit_width as u64; |
| 54 | let mask = if bw == 64 { u64::MAX } else { (1u64 << bw) - 1 }; |
| 55 | |
| 56 | // Pack values into the byte array, bit by bit. |
| 57 | let mut bit_offset: usize = 0; |
| 58 | for &val in values { |
| 59 | let residual = (val.wrapping_sub(min_val) as u64) & mask; |
| 60 | pack_bits(packed, bit_offset, residual, bit_width); |
| 61 | bit_offset += bit_width as usize; |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | /// Decode a single block from the byte stream. |
| 66 | /// |