Pack a slice of u32 values into a compact byte buffer. Layout: `[num_values: u16 LE][bit_width: u8][packed bits...]` Total bytes = 3 + ceil(num_values * bit_width / 8).
(values: &[u32])
| 23 | /// |
| 24 | /// Total bytes = 3 + ceil(num_values * bit_width / 8). |
| 25 | pub fn pack(values: &[u32]) -> Vec<u8> { |
| 26 | if values.is_empty() { |
| 27 | let mut buf = Vec::with_capacity(3); |
| 28 | buf.extend_from_slice(&0u16.to_le_bytes()); |
| 29 | buf.push(0); |
| 30 | return buf; |
| 31 | } |
| 32 | |
| 33 | let max_val = values.iter().copied().max().unwrap_or(0); |
| 34 | let bit_width = bits_needed(max_val); |
| 35 | |
| 36 | let total_bits = values.len() as u64 * bit_width as u64; |
| 37 | let data_bytes = total_bits.div_ceil(8) as usize; |
| 38 | let mut buf = Vec::with_capacity(3 + data_bytes); |
| 39 | |
| 40 | // Header. |
| 41 | buf.extend_from_slice(&(values.len() as u16).to_le_bytes()); |
| 42 | buf.push(bit_width); |
| 43 | |
| 44 | if bit_width == 0 { |
| 45 | // All values are 0 — no data bytes needed. |
| 46 | return buf; |
| 47 | } |
| 48 | |
| 49 | // Pack bits. |
| 50 | buf.resize(3 + data_bytes, 0); |
| 51 | let data = &mut buf[3..]; |
| 52 | let mut bit_pos = 0u64; |
| 53 | |
| 54 | for &val in values { |
| 55 | let byte_idx = (bit_pos / 8) as usize; |
| 56 | let bit_offset = (bit_pos % 8) as u32; |
| 57 | |
| 58 | // Write value starting at bit_offset within data[byte_idx..]. |
| 59 | // A single value may span up to 5 bytes (32 bits + 7 bit offset). |
| 60 | let wide = (val as u64) << bit_offset; |
| 61 | let bytes = wide.to_le_bytes(); |
| 62 | let bytes_to_write = (bit_offset + bit_width as u32).div_ceil(8) as usize; |
| 63 | for i in 0..bytes_to_write.min(data.len() - byte_idx) { |
| 64 | data[byte_idx + i] |= bytes[i]; |
| 65 | } |
| 66 | |
| 67 | bit_pos += bit_width as u64; |
| 68 | } |
| 69 | |
| 70 | buf |
| 71 | } |
| 72 | |
| 73 | /// Unpack values from a bitpacked buffer. |
| 74 | /// |