#[allow(dead_code)]
pub(crate) const MAX_VARINT_BYTES: usize = 5;
const CONTINUATION_BIT: u8 = 0x80;
const PAYLOAD_BITS: u32 = 7;
const PAYLOAD_MASK: u8 = 0x7f;
#[inline]
pub(crate) fn push_varint(out: &mut Vec<u8>, mut v: u32) {
loop {
let byte = (v as u8) & PAYLOAD_MASK;
v >>= PAYLOAD_BITS;
if v == 0 {
out.push(byte);
return;
}
out.push(byte | CONTINUATION_BIT);
}
}
#[inline]
pub(crate) fn read_varint(bytes: &[u8], at: &mut usize) -> Option<u32> {
let mut v: u32 = 0;
let mut shift: u32 = 0;
loop {
let &b = bytes.get(*at)?;
*at += 1;
let payload = (b & PAYLOAD_MASK) as u32;
v |= payload.checked_shl(shift)?;
if shift > 0 && payload >> (32 - shift.min(32)) != 0 {
return None;
}
if b & CONTINUATION_BIT == 0 {
return Some(v);
}
shift += PAYLOAD_BITS;
if shift >= 32 + PAYLOAD_BITS {
return None;
}
}
}
pub(crate) fn encode_run(out: &mut Vec<u8>, positions: &[u32]) {
let mut prev: u32 = 0;
for (i, &p) in positions.iter().enumerate() {
debug_assert!(i == 0 || p > prev, "positions must be strictly increasing");
let delta = if i == 0 { p } else { p - prev };
push_varint(out, delta);
prev = p;
}
}
#[allow(dead_code)]
pub(crate) fn decode_run(bytes: &[u8], at: &mut usize, tf: u32, out: &mut Vec<u32>) -> Option<()> {
let mut prev: u32 = 0;
for i in 0..tf {
let delta = read_varint(bytes, at)?;
let p = if i == 0 {
delta
} else {
prev.checked_add(delta)?
};
out.push(p);
prev = p;
}
Some(())
}
pub(crate) fn skip_run(bytes: &[u8], at: &mut usize, tf: u32) -> Option<()> {
for _ in 0..tf {
read_varint(bytes, at)?;
}
Some(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn varint_round_trips_boundaries() {
for v in [0u32, 1, 127, 128, 16383, 16384, u32::MAX - 1, u32::MAX] {
let mut buf = Vec::new();
push_varint(&mut buf, v);
assert!(buf.len() <= MAX_VARINT_BYTES);
let mut at = 0;
assert_eq!(read_varint(&buf, &mut at), Some(v));
assert_eq!(at, buf.len());
}
}
#[test]
fn read_varint_rejects_truncation() {
let mut buf = Vec::new();
push_varint(&mut buf, 300);
let mut at = 0;
assert_eq!(read_varint(&buf[..1], &mut at), None);
}
#[test]
fn read_varint_rejects_overflow() {
let buf = [0xff, 0xff, 0xff, 0xff, 0xff, 0x01];
let mut at = 0;
assert_eq!(read_varint(&buf, &mut at), None);
}
#[test]
fn run_round_trips() {
let positions = [3u32, 4, 9, 100, 1_000_000];
let mut buf = Vec::new();
encode_run(&mut buf, &positions);
let mut at = 0;
let mut got = Vec::new();
decode_run(&buf, &mut at, positions.len() as u32, &mut got).expect("decode");
assert_eq!(got, positions);
assert_eq!(at, buf.len());
}
#[test]
fn runs_concatenate_and_skip() {
let a = [5u32, 6];
let b = [0u32, 2, 4];
let mut buf = Vec::new();
encode_run(&mut buf, &a);
let a_end = buf.len();
encode_run(&mut buf, &b);
let mut at = 0;
skip_run(&buf, &mut at, a.len() as u32).expect("skip");
assert_eq!(at, a_end);
let mut got = Vec::new();
decode_run(&buf, &mut at, b.len() as u32, &mut got).expect("decode");
assert_eq!(got, b);
}
#[test]
fn decode_run_rejects_truncated_tail() {
let mut buf = Vec::new();
encode_run(&mut buf, &[1u32, 2, 3]);
let mut at = 0;
let mut got = Vec::new();
assert_eq!(
decode_run(&buf[..buf.len() - 1], &mut at, 3, &mut got),
None
);
}
}