use super::bits::{pack_bits, unpack_bits};
use crate::error::CodecError;
pub(super) const BLOCK_HEADER_SIZE: usize = 11;
pub(super) fn encode_block(values: &[i64], out: &mut Vec<u8>) {
let count = values.len() as u16;
let mut min_val = values[0];
let mut max_val = values[0];
for &v in &values[1..] {
if v < min_val {
min_val = v;
}
if v > max_val {
max_val = v;
}
}
let range = (max_val as u128).wrapping_sub(min_val as u128) as u64;
let bit_width = if range == 0 {
0u8
} else {
64 - range.leading_zeros() as u8
};
out.extend_from_slice(&count.to_le_bytes());
out.push(bit_width);
out.extend_from_slice(&min_val.to_le_bytes());
if bit_width == 0 {
return;
}
let packed_bytes = (count as usize * bit_width as usize).div_ceil(8);
let pack_start = out.len();
out.resize(pack_start + packed_bytes, 0);
let packed = &mut out[pack_start..];
let bw = bit_width as u64;
let mask = if bw == 64 { u64::MAX } else { (1u64 << bw) - 1 };
let mut bit_offset: usize = 0;
for &val in values {
let residual = (val.wrapping_sub(min_val) as u64) & mask;
pack_bits(packed, bit_offset, residual, bit_width);
bit_offset += bit_width as usize;
}
}
pub(super) fn decode_block(
data: &[u8],
offset: usize,
values: &mut Vec<i64>,
block_idx: usize,
) -> Result<usize, CodecError> {
if offset + BLOCK_HEADER_SIZE > data.len() {
return Err(CodecError::Truncated {
expected: offset + BLOCK_HEADER_SIZE,
actual: data.len(),
});
}
let count = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
let bit_width = data[offset + 2];
let min_val = i64::from_le_bytes([
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
data[offset + 8],
data[offset + 9],
data[offset + 10],
]);
let mut pos = offset + BLOCK_HEADER_SIZE;
if bit_width == 0 {
values.extend(std::iter::repeat_n(min_val, count));
return Ok(pos);
}
if bit_width > 64 {
return Err(CodecError::Corrupt {
detail: format!("block {block_idx}: invalid bit_width {bit_width}"),
});
}
let packed_bytes = (count * bit_width as usize).div_ceil(8);
if pos + packed_bytes > data.len() {
return Err(CodecError::Truncated {
expected: pos + packed_bytes,
actual: data.len(),
});
}
let packed = &data[pos..pos + packed_bytes];
let mask: u64 = if bit_width == 64 {
u64::MAX
} else {
(1u64 << bit_width) - 1
};
let mut bit_offset: usize = 0;
for _ in 0..count {
let residual = unpack_bits(packed, bit_offset, bit_width) & mask;
values.push(min_val.wrapping_add(residual as i64));
bit_offset += bit_width as usize;
}
pos += packed_bytes;
Ok(pos)
}
pub(super) fn skip_block(
data: &[u8],
offset: usize,
block_idx: usize,
) -> Result<usize, CodecError> {
if offset + BLOCK_HEADER_SIZE > data.len() {
return Err(CodecError::Truncated {
expected: offset + BLOCK_HEADER_SIZE,
actual: data.len(),
});
}
let count = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
let bit_width = data[offset + 2];
if bit_width > 64 {
return Err(CodecError::Corrupt {
detail: format!("block {block_idx}: invalid bit_width {bit_width}"),
});
}
let packed_bytes = if bit_width == 0 {
0
} else {
(count * bit_width as usize).div_ceil(8)
};
Ok(offset + BLOCK_HEADER_SIZE + packed_bytes)
}
pub fn bit_width_for_range(min: i64, max: i64) -> u8 {
let range = (max as u128).wrapping_sub(min as u128) as u64;
if range == 0 {
0
} else {
64 - range.leading_zeros() as u8
}
}