pub const NORM_BUCKETS: usize = 256;
#[must_use]
pub fn encode_length(length: u32) -> u8 {
if length < 40 {
return u8::try_from(length).unwrap_or(u8::MAX);
}
let mut bucket = 40_u32;
let mut value = 40_u32;
let mut step = 1_u32;
let mut since = 0_u32;
while bucket < 255 {
value = value.saturating_add(step);
if length < value {
return u8::try_from(bucket).unwrap_or(u8::MAX);
}
bucket += 1;
since += 1;
if since == 8 {
since = 0;
step = step.saturating_mul(2);
}
}
u8::MAX
}
#[must_use]
pub fn decode_length(norm: u8) -> u32 {
let target = u32::from(norm);
if target < 40 {
return target;
}
let mut bucket = 40_u32;
let mut value = 40_u32;
let mut step = 1_u32;
let mut since = 0_u32;
while bucket < 255 {
value = value.saturating_add(step);
if bucket == target {
return value.saturating_sub(1);
}
bucket += 1;
since += 1;
if since == 8 {
since = 0;
step = step.saturating_mul(2);
}
}
u32::MAX
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Norms {
bytes: Vec<u8>,
}
impl Norms {
#[must_use]
pub const fn new() -> Self {
Self { bytes: Vec::new() }
}
#[must_use]
pub fn from_lengths(lengths: &[u32]) -> Self {
Self {
bytes: lengths.iter().copied().map(encode_length).collect(),
}
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
#[must_use]
pub const fn from_bytes(bytes: Vec<u8>) -> Self {
Self { bytes }
}
#[must_use]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
#[must_use]
pub fn length(&self, row: u32) -> Option<u32> {
let index = usize::try_from(row).ok()?;
self.bytes.get(index).copied().map(decode_length)
}
}
#[cfg(test)]
#[allow(
clippy::expect_used,
clippy::indexing_slicing,
clippy::panic,
clippy::unwrap_used
)]
mod tests {
use super::*;
#[test]
fn encoding_is_monotone_non_decreasing_in_length() {
let mut previous = 0_u8;
let mut length = 0_u32;
while length < 1_000_000 {
let encoded = encode_length(length);
assert!(
encoded >= previous,
"encoding fell from {previous} to {encoded} at length {length}"
);
previous = encoded;
length += if length < 128 { 1 } else { length / 16 };
}
}
#[test]
fn short_documents_are_encoded_exactly() {
for length in 0..40_u32 {
assert_eq!(encode_length(length), u8::try_from(length).expect("small"));
assert_eq!(decode_length(encode_length(length)), length);
}
}
#[test]
fn decoding_never_understates_the_true_length() {
let mut length = 0_u32;
while length < 5_000_000 {
let restored = decode_length(encode_length(length));
assert!(
restored >= length,
"decoded {restored} is below the true length {length}"
);
length += if length < 256 { 1 } else { length / 8 };
}
}
#[test]
fn the_full_byte_range_is_reachable_and_saturating() {
assert_eq!(encode_length(u32::MAX), u8::MAX);
assert_eq!(decode_length(u8::MAX), u32::MAX);
assert_eq!(encode_length(0), 0);
}
#[test]
fn norms_index_by_row_id() {
let norms = Norms::from_lengths(&[0, 5, 39, 4096]);
assert_eq!(norms.len(), 4);
assert!(!norms.is_empty());
assert_eq!(norms.length(0), Some(0));
assert_eq!(norms.length(1), Some(5));
assert_eq!(norms.length(2), Some(39));
assert!(norms.length(3).expect("row 3") >= 4096);
assert_eq!(norms.length(4), None);
assert!(Norms::new().is_empty());
}
#[test]
fn norm_bytes_round_trip_through_the_byte_array() {
let norms = Norms::from_lengths(&[1, 2, 3]);
let restored = Norms::from_bytes(norms.as_bytes().to_vec());
assert_eq!(norms, restored);
}
}