automerge/rust/automerge/src/columnar/encoding/leb128.rs
Conrad Irwin 98e755106f
Fix and simplify lebsize calculations (#503)
Before this change numbits_i64() was incorrect for every value of the
form 0 - 2^x. This only manifested in a visible error if x%7 == 6 (so
for -64, -8192, etc.) at which point `lebsize` would return a value one
too large, causing a panic in commit().
2023-01-23 11:01:05 +00:00

82 lines
2 KiB
Rust

/// The number of bytes required to encode `val` as a LEB128 integer
pub(crate) fn lebsize(mut val: i64) -> u64 {
if val < 0 {
val = !val
}
// 1 extra for the sign bit
leb_bytes(1 + 64 - val.leading_zeros() as u64)
}
/// The number of bytes required to encode `val` as a uLEB128 integer
pub(crate) fn ulebsize(val: u64) -> u64 {
if val == 0 {
return 1;
}
leb_bytes(64 - val.leading_zeros() as u64)
}
fn leb_bytes(bits: u64) -> u64 {
(bits + 6) / 7
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn test_ulebsize(val in 0..u64::MAX) {
let mut out = Vec::new();
leb128::write::unsigned(&mut out, val).unwrap();
let expected = out.len() as u64;
assert_eq!(expected, ulebsize(val))
}
#[test]
fn test_lebsize(val in i64::MIN..i64::MAX) {
let mut out = Vec::new();
leb128::write::signed(&mut out, val).unwrap();
let expected = out.len() as u64;
assert_eq!(expected, lebsize(val))
}
}
#[test]
fn ulebsize_examples() {
let scenarios = vec![0, 1, 127, 128, 129, 169, u64::MAX];
for val in scenarios {
let mut out = Vec::new();
leb128::write::unsigned(&mut out, val).unwrap();
let expected = out.len() as u64;
assert_eq!(ulebsize(val), expected, "value: {}", val)
}
}
#[test]
fn lebsize_examples() {
let scenarios = vec![
0,
1,
-1,
63,
64,
-64,
-65,
127,
128,
-127,
-128,
-2097152,
169,
i64::MIN,
i64::MAX,
];
for val in scenarios {
let mut out = Vec::new();
leb128::write::signed(&mut out, val).unwrap();
let expected = out.len() as u64;
assert_eq!(lebsize(val), expected, "value: {}", val)
}
}
}