use crate::{Vec, compact::Numeric, vec};
pub const THRESHOLDS: [(usize, u8, u8, u64, u64); 7] = [
(1, 0x80, 0xc0, 0x100, 0x4000),
(2, 0xc0, 0xe0, 0x10000, 0x200000),
(3, 0xe0, 0xf0, 0x1000000, 0x10000000),
(4, 0xf0, 0xf8, 0x100000000, 0x800000000),
(5, 0xf8, 0xfc, 0x10000000000, 0x40000000000),
(6, 0xfc, 0xfe, 0x1000000000000, 0x2000000000000),
(7, 0xfe, 0xff, 0x100000000000000, 0x100000000000000),
];
pub fn encode(value: u64) -> Vec<u8> {
if value < 0x80 {
return vec![value as u8];
}
for (length, base, _, bits, threshold) in THRESHOLDS.into_iter() {
if value < threshold {
let mut encoded = vec![base + (value / bits) as u8];
let remainder = (value % bits).encode();
encoded.extend_from_slice(&remainder);
while encoded.len() < length + 1 {
encoded.push(0);
}
return encoded;
}
}
[vec![255], value.to_le_bytes().to_vec()].concat()
}
pub fn decode(encoded: &[u8]) -> u64 {
self::decode_from(encoded).0
}
pub fn decode_from(encoded: &[u8]) -> (u64, usize) {
if encoded.is_empty() {
return (0, 0);
}
let prefix = encoded[0];
if prefix < 0x80 {
return (prefix as u64, 1);
}
for (length, base, next, bits, _) in THRESHOLDS.into_iter() {
if prefix < next {
let dlen = length + 1;
return (
((prefix - base) as u64) * bits + u64::decode(&encoded[1..dlen]),
dlen,
);
}
}
(u64::decode(&encoded[1..9]), 9)
}
#[test]
fn thresholds() {
for (length, base, _, bits, threshold) in THRESHOLDS.into_iter() {
assert_eq!(bits.trailing_zeros(), 8 * length as u32);
let expected = 2u64.pow(8_u32) - 2u64.pow((8 - length) as u32);
assert_eq!(base, expected as u8);
let expected = 2u64.pow(7 * (length + 1) as u32);
assert_eq!(threshold, expected,);
}
}
#[test]
fn roundtrip() {
for (length, _, _, _, threshold) in THRESHOLDS.iter() {
let value = threshold - 1;
let encoded = encode(value);
let (decoded, dlen) = decode_from(&encoded);
assert_eq!(dlen, *length + 1);
assert_eq!(encoded.len(), dlen);
assert_eq!(value, decoded);
}
}
#[test]
fn vlen_zero() {
assert_eq!(encode(0), vec![0]);
}