#![forbid(unsafe_code)]
#![cfg_attr(not(feature = "u64"), allow(unreachable_pub))]
#[allow(unused_imports)] use alloc::{vec, vec::Vec};
pub const MAX_BYTES: usize = 9;
const TAG_THRESHOLD: u8 = 248;
const NUM_TIERS: usize = 8;
const fn tier_offset(n: usize) -> u64 {
if n == 0 {
return 0;
}
if n == 1 {
return TAG_THRESHOLD as u64;
}
let mut result = TAG_THRESHOLD as u64;
let mut power = 1u64; let mut i = 2;
while i <= n {
power = power.saturating_mul(256);
result = result.saturating_add(power);
i += 1;
}
result
}
const OFFSETS: [u64; NUM_TIERS + 1] = [
tier_offset(0),
tier_offset(1),
tier_offset(2),
tier_offset(3),
tier_offset(4),
tier_offset(5),
tier_offset(6),
tier_offset(7),
tier_offset(8),
];
const BOUNDS: [u64; NUM_TIERS + 1] = [
tier_offset(1), tier_offset(2),
tier_offset(3),
tier_offset(4),
tier_offset(5),
tier_offset(6),
tier_offset(7),
tier_offset(8),
u64::MAX, ];
#[inline]
#[must_use]
pub const fn encoded_len(value: u64) -> usize {
if value < BOUNDS[0] {
return 1;
}
let bw = 64 - value.leading_zeros();
let candidate = ((bw - 1) / 8 + 2) as usize;
#[allow(clippy::indexing_slicing)]
if value < BOUNDS[candidate - 2] {
candidate - 1
} else {
candidate
}
}
#[allow(clippy::cast_possible_truncation, clippy::indexing_slicing)]
pub fn encode(value: u64, buf: &mut Vec<u8>) {
if value < BOUNDS[0] {
buf.push((value & 0xFF) as u8);
return;
}
let bw = 64 - value.leading_zeros();
let mut tier = ((bw - 1) / 8 + 1) as usize;
if value < BOUNDS[tier - 1] {
tier -= 1;
}
let tag = (TAG_THRESHOLD as usize + tier - 1) as u8;
let payload = (value - OFFSETS[tier]) << (8 * (NUM_TIERS - tier));
let pb = payload.to_be_bytes();
let original_len = buf.len();
buf.extend_from_slice(&[tag, pb[0], pb[1], pb[2], pb[3], pb[4], pb[5], pb[6], pb[7]]);
buf.truncate(original_len + tier + 1);
}
#[derive(Debug, Clone, Copy)]
pub struct EncodedU64 {
buf: [u8; MAX_BYTES],
len: u8,
}
impl PartialEq for EncodedU64 {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Eq for EncodedU64 {}
impl core::hash::Hash for EncodedU64 {
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.as_slice().hash(state);
}
}
impl PartialOrd for EncodedU64 {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for EncodedU64 {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_slice().cmp(other.as_slice())
}
}
impl EncodedU64 {
#[inline]
#[must_use]
#[cfg_attr(not(feature = "u64"), allow(clippy::trivially_copy_pass_by_ref))]
pub const fn len(&self) -> usize {
self.len as usize
}
#[inline]
#[must_use]
#[cfg_attr(
not(feature = "u64"),
allow(clippy::trivially_copy_pass_by_ref, clippy::unused_self)
)]
pub const fn is_empty(&self) -> bool {
false
}
#[inline]
#[must_use]
#[allow(clippy::indexing_slicing)] pub const fn as_slice(&self) -> &[u8] {
self.buf.split_at(self.len as usize).0
}
}
impl core::ops::Deref for EncodedU64 {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.as_slice()
}
}
impl AsRef<[u8]> for EncodedU64 {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl core::borrow::Borrow<[u8]> for EncodedU64 {
#[inline]
fn borrow(&self) -> &[u8] {
self.as_slice()
}
}
impl IntoIterator for EncodedU64 {
type Item = u8;
type IntoIter = core::iter::Take<core::array::IntoIter<u8, MAX_BYTES>>;
fn into_iter(self) -> Self::IntoIter {
let len = self.len();
self.buf.into_iter().take(len)
}
}
impl<'a> IntoIterator for &'a EncodedU64 {
type Item = &'a u8;
type IntoIter = core::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.as_slice().iter()
}
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::indexing_slicing)]
pub const fn encoded_bytes(value: u64) -> EncodedU64 {
if value < BOUNDS[0] {
return EncodedU64 {
buf: [(value & 0xFF) as u8, 0, 0, 0, 0, 0, 0, 0, 0],
len: 1,
};
}
let bw = 64 - value.leading_zeros();
let mut tier = ((bw - 1) / 8 + 1) as usize;
if value < BOUNDS[tier - 1] {
tier -= 1;
}
let tag = (TAG_THRESHOLD as usize + tier - 1) as u8;
let payload = (value - OFFSETS[tier]) << (8 * (NUM_TIERS - tier));
let pb = payload.to_be_bytes();
EncodedU64 {
buf: [tag, pb[0], pb[1], pb[2], pb[3], pb[4], pb[5], pb[6], pb[7]],
len: (tier + 1) as u8,
}
}
#[inline]
#[allow(clippy::many_single_char_names)] pub const fn decode(buf: &[u8]) -> Result<(u64, usize), DecodeError> {
let Some((&tag, rest)) = buf.split_first() else {
return Err(DecodeError::BufferTooShort);
};
if tag < TAG_THRESHOLD {
return Ok((tag as u64, 1));
}
let (offset, payload, consumed) = match tag {
0xF8 => match rest {
&[a, ..] => (OFFSETS[1], u64::from_be_bytes([0, 0, 0, 0, 0, 0, 0, a]), 2),
_ => return Err(DecodeError::BufferTooShort),
},
0xF9 => match rest {
&[a, b, ..] => (OFFSETS[2], u64::from_be_bytes([0, 0, 0, 0, 0, 0, a, b]), 3),
_ => return Err(DecodeError::BufferTooShort),
},
0xFA => match rest {
&[a, b, c, ..] => (OFFSETS[3], u64::from_be_bytes([0, 0, 0, 0, 0, a, b, c]), 4),
_ => return Err(DecodeError::BufferTooShort),
},
0xFB => match rest {
&[a, b, c, d, ..] => (OFFSETS[4], u64::from_be_bytes([0, 0, 0, 0, a, b, c, d]), 5),
_ => return Err(DecodeError::BufferTooShort),
},
0xFC => match rest {
&[a, b, c, d, e, ..] => (OFFSETS[5], u64::from_be_bytes([0, 0, 0, a, b, c, d, e]), 6),
_ => return Err(DecodeError::BufferTooShort),
},
0xFD => match rest {
&[a, b, c, d, e, f, ..] => {
(OFFSETS[6], u64::from_be_bytes([0, 0, a, b, c, d, e, f]), 7)
}
_ => return Err(DecodeError::BufferTooShort),
},
0xFE => match rest {
&[a, b, c, d, e, f, g, ..] => {
(OFFSETS[7], u64::from_be_bytes([0, a, b, c, d, e, f, g]), 8)
}
_ => return Err(DecodeError::BufferTooShort),
},
_ => match rest {
&[a, b, c, d, e, f, g, h, ..] => {
(OFFSETS[8], u64::from_be_bytes([a, b, c, d, e, f, g, h]), 9)
}
_ => return Err(DecodeError::BufferTooShort),
},
};
match offset.checked_add(payload) {
Some(value) => Ok((value, consumed)),
None => Err(DecodeError::Overflow),
}
}
#[derive(Debug)]
#[must_use = "iterators are lazy; consume with for/.collect/.next"]
pub struct DecodeIter<'a> {
cursor: &'a [u8],
fused_err: bool,
}
#[inline]
pub const fn decode_iter(buf: &[u8]) -> DecodeIter<'_> {
DecodeIter {
cursor: buf,
fused_err: false,
}
}
impl Iterator for DecodeIter<'_> {
type Item = Result<u64, DecodeError>;
fn next(&mut self) -> Option<Self::Item> {
if self.fused_err || self.cursor.is_empty() {
return None;
}
match decode(self.cursor) {
Ok((value, consumed)) => {
self.cursor = match self.cursor.get(consumed..) {
Some(rest) => rest,
None => &[],
};
Some(Ok(value))
}
Err(err) => {
self.fused_err = true;
Some(Err(err))
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.fused_err || self.cursor.is_empty() {
return (0, Some(0));
}
(1, Some(self.cursor.len()))
}
}
impl core::iter::FusedIterator for DecodeIter<'_> {}
#[cfg_attr(not(feature = "u64"), allow(dead_code))]
pub fn decode_all(buf: &[u8]) -> Result<Vec<u64>, DecodeError> {
decode_iter(buf).collect()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum DecodeError {
#[error("buffer too short for bijou64 encoding")]
BufferTooShort,
#[error("bijou64 tier 8 payload overflows u64")]
Overflow,
}
#[cfg(test)]
#[allow(clippy::indexing_slicing, clippy::needless_range_loop, clippy::panic)]
mod tests {
use super::*;
type TestResult = Result<(), DecodeError>;
mod offset_table {
use super::*;
#[test]
fn recurrence() {
assert_eq!(OFFSETS[0], 0);
assert_eq!(OFFSETS[1], 248);
let mut power = 1u64; for i in 2..=NUM_TIERS {
power *= 256;
assert_eq!(
OFFSETS[i],
OFFSETS[i - 1] + power,
"OFFSETS[{i}] does not satisfy recurrence"
);
}
}
#[test]
fn known_values() {
assert_eq!(OFFSETS[1], 248);
assert_eq!(OFFSETS[2], 504);
assert_eq!(OFFSETS[3], 0x1_01F8);
assert_eq!(OFFSETS[4], 0x101_01F8);
assert_eq!(OFFSETS[5], 0x1_0101_01F8);
assert_eq!(OFFSETS[6], 0x101_0101_01F8);
assert_eq!(OFFSETS[7], 0x1_0101_0101_01F8);
assert_eq!(OFFSETS[8], 0x101_0101_0101_01F8);
}
#[test]
fn bounds_are_consistent() {
assert_eq!(BOUNDS[0], 248);
for i in 1..NUM_TIERS {
assert_eq!(
BOUNDS[i],
OFFSETS[i + 1],
"BOUNDS[{i}] should equal OFFSETS[{}]",
i + 1
);
}
assert_eq!(BOUNDS[NUM_TIERS], u64::MAX);
}
}
mod round_trip {
use super::*;
#[test]
fn all_tier_boundaries() -> TestResult {
for tier in 1..=NUM_TIERS {
let min_val = OFFSETS[tier];
let max_val = if tier < NUM_TIERS {
OFFSETS[tier + 1] - 1
} else {
u64::MAX
};
let expected_len = 1 + tier;
for &value in &[min_val, max_val] {
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(
buf.len(),
expected_len,
"tier {tier} value {value}: expected {expected_len} bytes, got {}",
buf.len()
);
let (decoded, consumed) = decode(&buf)?;
assert_eq!(decoded, value, "tier {tier} round-trip failed for {value}");
assert_eq!(consumed, expected_len);
}
}
Ok(())
}
}
mod errors {
use super::*;
#[test]
fn empty_buffer() {
assert_eq!(decode(&[]), Err(DecodeError::BufferTooShort));
}
#[test]
fn truncated_at_every_tier() {
for tier in 1..=NUM_TIERS {
let tag = u8::try_from(247 + tier).unwrap_or(0xFF);
let mut buf = vec![tag];
buf.extend(core::iter::repeat_n(0x00u8, tier - 1));
assert_eq!(
decode(&buf),
Err(DecodeError::BufferTooShort),
"tier {tier} (tag 0x{tag:02X}) with {}-byte payload \
should be BufferTooShort",
tier - 1
);
}
}
#[test]
fn tier8_overflow() {
let buf = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
assert_eq!(decode(&buf), Err(DecodeError::Overflow));
}
#[test]
fn tier8_overflow_exact_boundary() -> TestResult {
let max_payload = u64::MAX - OFFSETS[8];
let overflow_payload = max_payload + 1; let be = overflow_payload.to_be_bytes();
let buf = [0xFF, be[0], be[1], be[2], be[3], be[4], be[5], be[6], be[7]];
assert_eq!(decode(&buf), Err(DecodeError::Overflow));
let be_max = max_payload.to_be_bytes();
let buf_max = [
0xFF, be_max[0], be_max[1], be_max[2], be_max[3], be_max[4], be_max[5], be_max[6],
be_max[7],
];
let (value, consumed) = decode(&buf_max)?;
assert_eq!(value, u64::MAX);
assert_eq!(consumed, 9);
Ok(())
}
#[test]
fn trailing_bytes_not_consumed() -> TestResult {
let (v, n) = decode(&[0x2A, 0xDE, 0xAD])?;
assert_eq!((v, n), (42, 1));
let (v, n) = decode(&[0xF8, 0x34, 0xBE, 0xEF])?;
assert_eq!((v, n), (300, 2));
Ok(())
}
#[test]
fn decode_always_advances() -> TestResult {
let pad = [0u8; MAX_BYTES + 7]; for first in 0u8..=255u8 {
let mut buf = Vec::with_capacity(1 + pad.len());
buf.push(first);
buf.extend_from_slice(&pad);
let (_, n) = decode(&buf)?;
assert!(
n >= 1,
"decode returned consumed = 0 on first byte {first:#04X}",
);
}
Ok(())
}
}
mod exhaustive {
use super::*;
#[test]
fn tier0() -> TestResult {
for value in 0..248u64 {
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(buf.len(), 1);
assert_eq!(buf[0], u8::try_from(value).unwrap_or(0));
let (decoded, consumed) = decode(&buf)?;
assert_eq!(decoded, value);
assert_eq!(consumed, 1);
}
Ok(())
}
#[test]
fn tier1() -> TestResult {
for value in 248..504u64 {
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(buf.len(), 2, "value {value} should encode in 2 bytes");
assert_eq!(buf[0], 0xF8);
let (decoded, consumed) = decode(&buf)?;
assert_eq!(decoded, value, "round-trip failed for {value}");
assert_eq!(consumed, 2);
}
Ok(())
}
#[test]
fn tier2() -> TestResult {
for value in 504..66_040u64 {
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(buf.len(), 3, "value {value} should encode in 3 bytes");
assert_eq!(buf[0], 0xF9);
let (decoded, consumed) = decode(&buf)?;
assert_eq!(decoded, value, "round-trip failed for {value}");
assert_eq!(consumed, 3);
}
Ok(())
}
#[test]
fn tier3_exhaustive() -> TestResult {
for value in 66_040u64..=16_843_255u64 {
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(buf.len(), 4, "value {value} should encode in 4 bytes");
assert_eq!(buf[0], 0xFA);
let (decoded, consumed) = decode(&buf)?;
assert_eq!(decoded, value, "round-trip failed for {value}");
assert_eq!(consumed, 4);
}
Ok(())
}
#[test]
fn canonicality_byte_sequence_exhaustive() -> TestResult {
let check = |buf: &[u8]| -> TestResult {
if let Ok((value, consumed)) = decode(buf) {
let mut re = Vec::with_capacity(MAX_BYTES);
encode(value, &mut re);
assert_eq!(
re.as_slice(),
&buf[..consumed],
"non-canonical: decode({:02X?}) = {value}, re-encode = {:02X?}",
&buf[..consumed],
re
);
}
Ok(())
};
for b in 0u8..=255u8 {
check(&[b])?;
}
for p in 0u8..=255u8 {
check(&[0xF8, p])?;
}
for p1 in 0u8..=255u8 {
for p2 in 0u8..=255u8 {
check(&[0xF9, p1, p2])?;
}
}
for p in 0u32..(1u32 << 24) {
let bytes = p.to_be_bytes();
check(&[0xFA, bytes[1], bytes[2], bytes[3]])?;
}
Ok(())
}
}
mod boundaries {
use super::*;
#[test]
fn offset_triples() -> TestResult {
for tier in 1..=NUM_TIERS {
let offset = OFFSETS[tier];
let tier_len = 1 + tier;
let prev_len = if tier == 1 { 1 } else { 1 + (tier - 1) };
let below = offset - 1;
let mut buf = Vec::new();
encode(below, &mut buf);
assert_eq!(
buf.len(),
prev_len,
"OFFSETS[{tier}] - 1 = {below}: expected {prev_len} bytes, got {}",
buf.len()
);
let (v, n) = decode(&buf)?;
assert_eq!(v, below);
assert_eq!(n, prev_len);
buf.clear();
encode(offset, &mut buf);
assert_eq!(
buf.len(),
tier_len,
"OFFSETS[{tier}] = {offset}: expected {tier_len} bytes, got {}",
buf.len()
);
let (v, n) = decode(&buf)?;
assert_eq!(v, offset);
assert_eq!(n, tier_len);
if offset < u64::MAX {
buf.clear();
encode(offset + 1, &mut buf);
assert_eq!(
buf.len(),
tier_len,
"OFFSETS[{tier}] + 1 = {}: expected {tier_len} bytes, got {}",
offset + 1,
buf.len()
);
let (v, n) = decode(&buf)?;
assert_eq!(v, offset + 1);
assert_eq!(n, tier_len);
}
}
Ok(())
}
#[test]
fn all_zero_payloads() -> TestResult {
for tier in 1..=NUM_TIERS {
let tag = u8::try_from(247 + tier).unwrap_or(0xFF);
let mut buf = vec![tag];
buf.extend(core::iter::repeat_n(0x00u8, tier));
let (value, consumed) = decode(&buf)?;
assert_eq!(
value, OFFSETS[tier],
"tier {tier} all-zeros payload: expected OFFSETS[{tier}] = {}, got {value}",
OFFSETS[tier]
);
assert_eq!(consumed, 1 + tier);
let mut re = Vec::new();
encode(value, &mut re);
assert_eq!(re, buf, "tier {tier} all-zeros round-trip mismatch");
}
Ok(())
}
#[test]
fn all_ones_payloads() -> TestResult {
for tier in 1..=NUM_TIERS {
let tag = u8::try_from(247 + tier).unwrap_or(0xFF);
let mut buf = vec![tag];
buf.extend(core::iter::repeat_n(0xFFu8, tier));
let result = decode(&buf);
if tier < NUM_TIERS {
let (value, consumed) = result?;
let expected = OFFSETS[tier + 1] - 1;
assert_eq!(
value, expected,
"tier {tier} all-ones payload: expected {expected}, got {value}"
);
assert_eq!(consumed, 1 + tier);
let mut re = Vec::new();
encode(value, &mut re);
assert_eq!(re, buf, "tier {tier} all-ones round-trip mismatch");
} else {
assert_eq!(
result,
Err(DecodeError::Overflow),
"tier 8 all-ones should overflow"
);
}
}
Ok(())
}
}
mod bijectivity {
use super::*;
#[test]
fn overlong_encoding_decodes_to_different_value() -> TestResult {
for tier in 1..NUM_TIERS {
let value = OFFSETS[tier]; let payload = value - OFFSETS[tier];
let wider_tier = tier + 1;
let tag = u8::try_from(247 + wider_tier).unwrap_or(0xFF);
let mut forged = vec![tag];
let be = payload.to_be_bytes();
forged.extend_from_slice(be.get(8 - wider_tier..).unwrap_or(&[]));
let (decoded, _) = decode(&forged)?;
assert_ne!(
decoded, value,
"tier {tier}: overlong encoding of {value} decoded back \
to {value} — bijectivity violated"
);
assert_eq!(
decoded, OFFSETS[wider_tier],
"tier {tier}: forged payload 0 in tier {wider_tier} should \
decode to OFFSETS[{wider_tier}]"
);
}
Ok(())
}
}
mod streaming {
use super::*;
#[test]
fn consecutive_decode() -> TestResult {
let values: &[(u64, u64)] = &[
(0, 0),
(42, 300),
(248, 504),
(0x101_01F8, u64::MAX),
(u64::MAX, 0),
];
for &(a, b) in values {
let mut buf = Vec::new();
encode(a, &mut buf);
encode(b, &mut buf);
let (decoded_a, consumed_a) = decode(&buf)?;
assert_eq!(decoded_a, a, "first value mismatch for ({a}, {b})");
let (decoded_b, consumed_b) = decode(&buf[consumed_a..])?;
assert_eq!(decoded_b, b, "second value mismatch for ({a}, {b})");
assert_eq!(
consumed_a + consumed_b,
buf.len(),
"total consumed mismatch for ({a}, {b})"
);
}
Ok(())
}
}
mod encoded_bytes {
use super::*;
#[test]
fn agrees_with_encode() {
let probes: &[u64] = &[
0,
1,
247,
248,
503,
504,
65_535,
66_039,
66_040,
0x1_01F8,
0x101_01F8,
u64::MAX,
];
for &v in probes {
let enc = encoded_bytes(v);
let mut via_encode = Vec::new();
encode(v, &mut via_encode);
assert_eq!(enc.len(), via_encode.len(), "len mismatch for {v}");
assert_eq!(&*enc, via_encode.as_slice(), "bytes mismatch for {v}");
}
}
#[test]
fn deref_asref_as_slice_consistent() {
let enc = encoded_bytes(300);
let via_deref: &[u8] = &enc;
let via_asref: &[u8] = enc.as_ref();
let via_as_slice: &[u8] = enc.as_slice();
assert_eq!(via_deref, &[0xF8, 0x34]);
assert_eq!(via_asref, &[0xF8, 0x34]);
assert_eq!(via_as_slice, &[0xF8, 0x34]);
}
#[test]
fn into_iter_walks_only_encoded_bytes() {
let enc = encoded_bytes(300); let collected: Vec<u8> = enc.into_iter().collect();
assert_eq!(collected, [0xF8, 0x34]);
}
#[test]
fn ref_into_iter_yields_encoded_bytes() {
let enc = encoded_bytes(300);
let collected: Vec<u8> = (&enc).into_iter().copied().collect();
assert_eq!(collected, [0xF8, 0x34]);
}
#[test]
fn copy_semantics() {
let enc = encoded_bytes(42);
let dup = enc;
assert_eq!(&*enc, &[0x2A]);
assert_eq!(&*dup, &[0x2A]);
}
#[test]
fn is_empty_is_always_false() {
for v in [0u64, 1, 247, 248, u64::MAX] {
assert!(!encoded_bytes(v).is_empty(), "is_empty true for {v}");
}
}
#[test]
fn borrow_impl() {
use alloc::collections::BTreeMap;
let mut map: BTreeMap<EncodedU64, &'static str> = BTreeMap::new();
map.insert(encoded_bytes(42), "the answer");
assert_eq!(map.get(&[0x2A][..]), Some(&"the answer"));
}
#[test]
fn eq_partial_eq() {
let a = encoded_bytes(300);
let b = encoded_bytes(300);
let c = encoded_bytes(301);
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn round_trip_via_decode() -> TestResult {
for v in [0u64, 247, 248, 65_535, u64::MAX] {
let enc = encoded_bytes(v);
let (decoded, n) = decode(&enc)?;
assert_eq!(decoded, v);
assert_eq!(n, enc.len());
}
Ok(())
}
}
mod iter {
use super::*;
#[test]
fn empty_buffer() {
let collected: Vec<_> = decode_iter(&[]).collect();
assert!(collected.is_empty());
}
#[test]
fn round_trip() -> TestResult {
let values: &[u64] = &[0, 1, 42, 247, 248, 503, 504, 65_535, 1u64 << 32, u64::MAX];
let mut buf = Vec::new();
for &v in values {
encode(v, &mut buf);
}
let collected: Result<Vec<u64>, _> = decode_iter(&buf).collect();
assert_eq!(collected?, values);
Ok(())
}
#[test]
fn fuses_after_error() {
let mut iter = decode_iter(&[0xF8]);
assert_eq!(iter.next(), Some(Err(DecodeError::BufferTooShort)));
assert_eq!(iter.next(), None, "iter must fuse after error");
assert_eq!(iter.next(), None, "iter must remain fused");
}
#[test]
fn overflow_error_then_fused() {
let buf = [0xFFu8; 9];
let mut iter = decode_iter(&buf);
assert_eq!(iter.next(), Some(Err(DecodeError::Overflow)));
assert_eq!(iter.next(), None);
}
#[test]
fn partial_success_then_error() {
let mut iter = decode_iter(&[0x42, 0xF8]);
assert_eq!(iter.next(), Some(Ok(0x42)));
assert_eq!(iter.next(), Some(Err(DecodeError::BufferTooShort)));
assert_eq!(iter.next(), None);
}
#[test]
fn size_hint() {
let empty = decode_iter(&[]);
assert_eq!(empty.size_hint(), (0, Some(0)));
let with_data = decode_iter(&[0x42, 0x99]);
assert_eq!(with_data.size_hint(), (1, Some(2)));
let mut errored = decode_iter(&[0xF8]);
let _ = errored.next();
assert_eq!(errored.size_hint(), (0, Some(0)));
}
#[test]
fn composable_with_combinators() {
let mut buf = Vec::new();
for v in [10u64, 20, 30, 40, 50] {
encode(v, &mut buf);
}
let sum: u64 = decode_iter(&buf).filter_map(Result::ok).sum();
assert_eq!(sum, 150);
let first_two: Vec<_> = decode_iter(&buf).take(2).filter_map(Result::ok).collect();
assert_eq!(first_two, [10, 20]);
}
}
mod decode_all {
use super::*;
#[test]
fn empty_buffer() -> TestResult {
assert_eq!(decode_all(&[])?, Vec::<u64>::new());
Ok(())
}
#[test]
fn round_trip() -> TestResult {
let values: &[u64] = &[0, 42, 300, 65_535, 1u64 << 32, u64::MAX];
let mut buf = Vec::new();
for &v in values {
encode(v, &mut buf);
}
assert_eq!(decode_all(&buf)?, values);
Ok(())
}
#[test]
fn short_circuits_on_first_error() {
assert_eq!(decode_all(&[0x42, 0xF8]), Err(DecodeError::BufferTooShort));
}
#[test]
fn overflow_propagates() {
assert_eq!(decode_all(&[0xFF; 9]), Err(DecodeError::Overflow));
}
#[test]
fn agrees_with_decode_iter_collect() {
let inputs: &[&[u8]] = &[
&[],
&[0x00],
&[0x42, 0xF8, 0x34],
&[0xF8], ];
for &input in inputs {
let via_iter: Result<Vec<u64>, _> = decode_iter(input).collect();
let via_all = decode_all(input);
assert_eq!(via_iter, via_all, "disagreement on {input:02X?}");
}
}
}
mod encode_api {
use super::*;
#[test]
fn appends_to_non_empty_buffer() -> TestResult {
let mut buf = vec![0xDE, 0xAD];
encode(300, &mut buf);
assert_eq!(&buf[..2], &[0xDE, 0xAD]);
let (value, consumed) = decode(&buf[2..])?;
assert_eq!(value, 300);
assert_eq!(consumed, 2);
assert_eq!(buf.len(), 4); Ok(())
}
#[test]
fn max_bytes_equals_encoded_len_of_max() {
let mut buf = Vec::new();
encode(u64::MAX, &mut buf);
assert_eq!(
buf.len(),
MAX_BYTES,
"MAX_BYTES disagrees with encode(u64::MAX).len()"
);
assert_eq!(encoded_len(u64::MAX), MAX_BYTES);
assert_eq!(encoded_bytes(u64::MAX).len(), MAX_BYTES);
}
}
mod test_vectors {
use super::*;
const VECTORS: &[(u64, &[u8])] = &[
(0, &[0x00]),
(1, &[0x01]),
(42, &[0x2A]),
(97, &[0x61]),
(127, &[0x7F]),
(128, &[0x80]),
(150, &[0x96]),
(247, &[0xF7]),
(248, &[0xF8, 0x00]),
(249, &[0xF8, 0x01]),
(300, &[0xF8, 0x34]),
(503, &[0xF8, 0xFF]),
(504, &[0xF9, 0x00, 0x00]),
(1_000, &[0xF9, 0x01, 0xF0]),
(65_535, &[0xF9, 0xFE, 0x07]),
(66_039, &[0xF9, 0xFF, 0xFF]),
(0x1_01F8, &[0xFA, 0x00, 0x00, 0x00]),
(0x1_05B8, &[0xFA, 0x00, 0x03, 0xC0]),
(0x101_01F7, &[0xFA, 0xFF, 0xFF, 0xFF]),
(0x101_01F8, &[0xFB, 0x00, 0x00, 0x00, 0x00]),
(0x1_0101_01F7, &[0xFB, 0xFF, 0xFF, 0xFF, 0xFF]),
(0x1_0101_01F8, &[0xFC, 0x00, 0x00, 0x00, 0x00, 0x00]),
(0x101_0101_01F7, &[0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]),
(0x101_0101_01F8, &[0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
(
0x1_0101_0101_01F7,
&[0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
),
(
0x1_0101_0101_01F8,
&[0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
),
(
0x101_0101_0101_01F7,
&[0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
),
(
0x101_0101_0101_01F8,
&[0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
),
(
u64::MAX,
&[0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x07],
),
];
#[test]
fn encode_vectors() {
for &(value, expected) in VECTORS {
let mut buf = Vec::new();
super::encode(value, &mut buf);
assert_eq!(
buf.as_slice(),
expected,
"encode({value}) mismatch: got {buf:02X?}, expected {expected:02X?}"
);
}
}
#[test]
fn decode_vectors() -> TestResult {
for &(expected_value, bytes) in VECTORS {
let (value, consumed) = super::decode(bytes)?;
assert_eq!(
value, expected_value,
"decode({bytes:02X?}): got {value}, expected {expected_value}"
);
assert_eq!(consumed, bytes.len());
}
Ok(())
}
}
#[cfg(feature = "bolero")]
mod property {
use super::*;
#[test]
#[cfg_attr(miri, ignore)]
fn round_trip() {
bolero::check!().with_arbitrary::<u64>().for_each(|&value| {
let mut buf = Vec::new();
encode(value, &mut buf);
let (decoded, consumed) = decode(&buf).unwrap_or_else(|e| {
panic!("round-trip decode failed for {value}: {e}");
});
assert_eq!(decoded, value, "round-trip failed for {value}");
assert_eq!(consumed, buf.len());
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn encoded_len_matches() {
bolero::check!().with_arbitrary::<u64>().for_each(|&value| {
let mut buf = Vec::new();
encode(value, &mut buf);
assert_eq!(encoded_len(value), buf.len());
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn encoded_bytes_matches() {
bolero::check!().with_arbitrary::<u64>().for_each(|&value| {
let mut buf = Vec::new();
encode(value, &mut buf);
let enc = encoded_bytes(value);
assert_eq!(&*enc, buf.as_slice(), "value {value}");
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn decode_never_panics() {
bolero::check!()
.with_arbitrary::<Vec<u8>>()
.for_each(|buf| {
let _ = decode(buf);
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn decode_always_advances() {
bolero::check!()
.with_arbitrary::<Vec<u8>>()
.for_each(|buf| {
if let Ok((_, consumed)) = decode(buf) {
assert!(consumed >= 1, "decode of {buf:02X?} returned consumed = 0");
}
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn lexicographic_order() {
bolero::check!()
.with_arbitrary::<(u64, u64)>()
.for_each(|&(a, b)| {
let enc_a = encoded_bytes(a);
let enc_b = encoded_bytes(b);
let slice_a: &[u8] = &enc_a;
let slice_b: &[u8] = &enc_b;
assert_eq!(
a.cmp(&b),
slice_a.cmp(slice_b),
"order mismatch: {a} vs {b}, \
encoded {slice_a:02X?} vs {slice_b:02X?}",
);
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn bijective() {
bolero::check!()
.with_arbitrary::<Vec<u8>>()
.for_each(|buf| {
if let Ok((value, consumed)) = decode(buf) {
let mut re_encoded = Vec::new();
encode(value, &mut re_encoded);
assert_eq!(
re_encoded.as_slice(),
buf.get(..consumed).unwrap_or_default(),
"bijection violated: decode({:02X?}) = {value}, \
re-encode = {:02X?}",
buf.get(..consumed).unwrap_or_default(),
re_encoded
);
}
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn decode_all_roundtrips_arbitrary_streams() {
bolero::check!()
.with_arbitrary::<Vec<u64>>()
.for_each(|xs| {
let mut buf = Vec::new();
for &x in xs {
encode(x, &mut buf);
}
assert_eq!(decode_all(&buf).as_deref(), Ok(xs.as_slice()));
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn decode_iter_matches_manual_cursor() {
bolero::check!()
.with_arbitrary::<Vec<u8>>()
.for_each(|buf| {
let via_iter: Vec<Result<u64, DecodeError>> = decode_iter(buf).collect();
let mut manual = Vec::new();
let mut cursor: &[u8] = buf;
loop {
if cursor.is_empty() {
break;
}
match decode(cursor) {
Ok((v, n)) => {
manual.push(Ok(v));
cursor = cursor.get(n..).unwrap_or_default();
}
Err(e) => {
manual.push(Err(e));
break; }
}
}
assert_eq!(
via_iter, manual,
"decode_iter disagreed with manual loop on {buf:02X?}"
);
});
}
#[test]
#[cfg_attr(miri, ignore)]
fn decode_iter_fuses_after_any_error() {
bolero::check!()
.with_arbitrary::<Vec<u8>>()
.for_each(|buf| {
let mut it = decode_iter(buf);
let mut seen_err = false;
for item in it.by_ref() {
assert!(
!seen_err,
"decode_iter yielded after an error on {buf:02X?}"
);
if item.is_err() {
seen_err = true;
}
}
if seen_err {
assert!(it.next().is_none(), "must stay fused");
assert!(it.next().is_none(), "must stay fused");
}
});
}
}
}