use std::fmt;
use super::arena_manager::ArenaSlot;
use super::compact_encoding::{write_varint_to_vec, VARINT_LEN_BIAS};
pub const FLAG_CROSS_ARENA: u8 = 0x01;
pub const CROSS_ARENA_SIZE: usize = 9;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RelativeEncodingError {
EmptyInput,
TruncatedFullPointer { actual_len: usize },
TruncatedVarint {
expected_len: usize,
actual_len: usize,
},
InvalidFullPointerFlag { flag: u8 },
OddRelativeTag { value: u64 },
RelativeDeltaTooLarge { value: u64 },
RelativeUnderflow { parent: ArenaSlot, delta: u32 },
InvalidRelativeDirection { parent: ArenaSlot, child: ArenaSlot },
SequentialIndexTooLarge { index: usize },
SequentialOverflow {
first_child: ArenaSlot,
index: usize,
},
}
impl fmt::Display for RelativeEncodingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyInput => write!(f, "empty relative pointer input"),
Self::TruncatedFullPointer { actual_len } => write!(
f,
"truncated full pointer: got {actual_len} bytes, need {CROSS_ARENA_SIZE}"
),
Self::TruncatedVarint {
expected_len,
actual_len,
} => write!(
f,
"truncated varint: got {actual_len} bytes, need {expected_len}"
),
Self::InvalidFullPointerFlag { flag } => {
write!(f, "invalid full pointer flag byte: {flag:#04x}")
}
Self::OddRelativeTag { value } => {
write!(f, "relative pointer has full-pointer tag bit set: {value}")
}
Self::RelativeDeltaTooLarge { value } => {
write!(f, "relative delta exceeds u32 slot range: {value}")
}
Self::RelativeUnderflow { parent, delta } => write!(
f,
"relative delta {delta} underflows parent slot {:?}",
parent
),
Self::InvalidRelativeDirection { parent, child } => write!(
f,
"child slot {:?} cannot be relatively encoded from parent {:?}",
child, parent
),
Self::SequentialIndexTooLarge { index } => {
write!(f, "sequential sibling index exceeds u32: {index}")
}
Self::SequentialOverflow { first_child, index } => write!(
f,
"sequential sibling index {index} overflows first child {:?}",
first_child
),
}
}
}
impl std::error::Error for RelativeEncodingError {}
pub type RelativeEncodingResult<T> = std::result::Result<T, RelativeEncodingError>;
pub const FLAG_RELATIVE_OFFSETS: u8 = 0x80;
pub const FLAG_SEQUENTIAL_SIBLINGS: u8 = 0x40;
pub fn encode_child_pointer(parent: ArenaSlot, child: ArenaSlot, out: &mut Vec<u8>) -> usize {
if parent.arena_id == child.arena_id {
if let Some(delta) = parent.slot_id.checked_sub(child.slot_id) {
encode_relative(delta, out)
} else {
encode_full(child, out)
}
} else {
encode_full(child, out)
}
}
pub fn try_encode_child_pointer(
parent: ArenaSlot,
child: ArenaSlot,
out: &mut Vec<u8>,
) -> RelativeEncodingResult<usize> {
if parent.arena_id == child.arena_id {
let delta = parent
.slot_id
.checked_sub(child.slot_id)
.ok_or(RelativeEncodingError::InvalidRelativeDirection { parent, child })?;
Ok(encode_relative(delta, out))
} else {
Ok(encode_full(child, out))
}
}
#[inline]
pub fn encode_relative(delta: u32, out: &mut Vec<u8>) -> usize {
let value = (delta as u64) << 1;
let start_len = out.len();
write_varint_to_vec(value, out);
out.len() - start_len
}
#[inline]
pub fn encode_full(slot: ArenaSlot, out: &mut Vec<u8>) -> usize {
out.push(FLAG_CROSS_ARENA);
out.extend_from_slice(&slot.arena_id.to_le_bytes());
out.extend_from_slice(&slot.slot_id.to_le_bytes());
CROSS_ARENA_SIZE
}
pub fn decode_child_pointer(data: &[u8], parent: ArenaSlot) -> (ArenaSlot, usize) {
try_decode_child_pointer(data, parent).expect("invalid relative child pointer encoding")
}
pub fn try_decode_child_pointer(
data: &[u8],
parent: ArenaSlot,
) -> RelativeEncodingResult<(ArenaSlot, usize)> {
let first = *data.first().ok_or(RelativeEncodingError::EmptyInput)?;
if first == FLAG_CROSS_ARENA {
try_decode_full(data)
} else {
let (delta, len) = try_decode_relative(data)?;
let child_slot = parent
.slot_id
.checked_sub(delta)
.ok_or(RelativeEncodingError::RelativeUnderflow { parent, delta })?;
Ok((ArenaSlot::new(parent.arena_id, child_slot), len))
}
}
#[inline]
pub fn decode_relative(data: &[u8]) -> (u32, usize) {
try_decode_relative(data).expect("invalid relative offset encoding")
}
#[inline]
pub fn try_decode_relative(data: &[u8]) -> RelativeEncodingResult<(u32, usize)> {
let (value, len) = try_read_varint_from_slice(data)?;
if value & FLAG_CROSS_ARENA as u64 != 0 {
return Err(RelativeEncodingError::OddRelativeTag { value });
}
let delta = value >> 1;
if delta > u32::MAX as u64 {
return Err(RelativeEncodingError::RelativeDeltaTooLarge { value: delta });
}
Ok((delta as u32, len))
}
#[inline]
pub fn decode_full(data: &[u8]) -> (ArenaSlot, usize) {
try_decode_full(data).expect("invalid full child pointer encoding")
}
#[inline]
pub fn try_decode_full(data: &[u8]) -> RelativeEncodingResult<(ArenaSlot, usize)> {
let first = *data.first().ok_or(RelativeEncodingError::EmptyInput)?;
if first != FLAG_CROSS_ARENA {
return Err(RelativeEncodingError::InvalidFullPointerFlag { flag: first });
}
if data.len() < CROSS_ARENA_SIZE {
return Err(RelativeEncodingError::TruncatedFullPointer {
actual_len: data.len(),
});
}
let arena_id = u32::from_le_bytes([data[1], data[2], data[3], data[4]]);
let slot_id = u32::from_le_bytes([data[5], data[6], data[7], data[8]]);
Ok((ArenaSlot::new(arena_id, slot_id), CROSS_ARENA_SIZE))
}
#[inline]
fn try_read_varint_from_slice(data: &[u8]) -> RelativeEncodingResult<(u64, usize)> {
let first = *data.first().ok_or(RelativeEncodingError::EmptyInput)?;
if first <= VARINT_LEN_BIAS {
Ok((first as u64, 1))
} else {
let len = (first - VARINT_LEN_BIAS) as usize;
let expected_len = 1 + len;
if data.len() < expected_len {
return Err(RelativeEncodingError::TruncatedVarint {
expected_len,
actual_len: data.len(),
});
}
let mut bytes = [0u8; 8];
bytes[..len].copy_from_slice(&data[1..expected_len]);
Ok((u64::from_le_bytes(bytes), expected_len))
}
}
#[inline]
pub fn is_same_arena(parent: ArenaSlot, child: ArenaSlot) -> bool {
parent.arena_id == child.arena_id
}
pub fn encoded_size(parent: ArenaSlot, child: ArenaSlot) -> usize {
if parent.arena_id == child.arena_id && child.slot_id <= parent.slot_id {
let delta = parent.slot_id - child.slot_id;
let value = (delta as u64) << 1;
super::compact_encoding::varint_size(value)
} else {
CROSS_ARENA_SIZE
}
}
pub fn try_encoded_size(parent: ArenaSlot, child: ArenaSlot) -> RelativeEncodingResult<usize> {
if parent.arena_id == child.arena_id {
let delta = parent
.slot_id
.checked_sub(child.slot_id)
.ok_or(RelativeEncodingError::InvalidRelativeDirection { parent, child })?;
Ok(super::compact_encoding::varint_size((delta as u64) << 1))
} else {
Ok(CROSS_ARENA_SIZE)
}
}
pub fn encode_children(parent: ArenaSlot, children: &[ArenaSlot], out: &mut Vec<u8>) -> usize {
let mut total = 0;
for &child in children {
total += encode_child_pointer(parent, child, out);
}
total
}
pub fn try_encode_children(
parent: ArenaSlot,
children: &[ArenaSlot],
out: &mut Vec<u8>,
) -> RelativeEncodingResult<usize> {
let mut total = 0;
for &child in children {
total += try_encode_child_pointer(parent, child, out)?;
}
Ok(total)
}
pub fn decode_children(data: &[u8], parent: ArenaSlot, count: usize) -> (Vec<ArenaSlot>, usize) {
try_decode_children(data, parent, count).expect("invalid relative child list encoding")
}
pub fn try_decode_children(
data: &[u8],
parent: ArenaSlot,
count: usize,
) -> RelativeEncodingResult<(Vec<ArenaSlot>, usize)> {
let mut children = Vec::with_capacity(count);
let mut offset = 0;
for _ in 0..count {
let (child, len) = try_decode_child_pointer(&data[offset..], parent)?;
children.push(child);
offset += len;
}
Ok((children, offset))
}
pub fn encode_sequential_siblings(
parent: ArenaSlot,
first_child: ArenaSlot,
out: &mut Vec<u8>,
) -> usize {
encode_child_pointer(parent, first_child, out)
}
pub fn try_encode_sequential_siblings(
parent: ArenaSlot,
first_child: ArenaSlot,
out: &mut Vec<u8>,
) -> RelativeEncodingResult<usize> {
try_encode_child_pointer(parent, first_child, out)
}
pub fn decode_sequential_siblings(
data: &[u8],
parent: ArenaSlot,
count: usize,
) -> (Vec<ArenaSlot>, usize) {
try_decode_sequential_siblings(data, parent, count)
.expect("invalid sequential sibling encoding")
}
pub fn try_decode_sequential_siblings(
data: &[u8],
parent: ArenaSlot,
count: usize,
) -> RelativeEncodingResult<(Vec<ArenaSlot>, usize)> {
if count == 0 {
return Ok((Vec::new(), 0));
}
let (first_child, bytes_consumed) = try_decode_child_pointer(data, parent)?;
let mut children = Vec::with_capacity(count);
for i in 0..count {
let offset = u32::try_from(i)
.map_err(|_| RelativeEncodingError::SequentialIndexTooLarge { index: i })?;
let slot_id = first_child.slot_id.checked_add(offset).ok_or(
RelativeEncodingError::SequentialOverflow {
first_child,
index: i,
},
)?;
children.push(ArenaSlot::new(first_child.arena_id, slot_id));
}
Ok((children, bytes_consumed))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_same_arena_relative_encoding() {
let parent = ArenaSlot::new(0, 100);
let child = ArenaSlot::new(0, 95);
let mut buf = Vec::new();
let len = encode_child_pointer(parent, child, &mut buf);
assert_eq!(len, 1);
assert_eq!(buf.len(), 1);
assert_eq!(buf[0], 10);
let (decoded, consumed) = decode_child_pointer(&buf, parent);
assert_eq!(consumed, 1);
assert_eq!(decoded.arena_id, 0);
assert_eq!(decoded.slot_id, 95);
}
#[test]
fn test_cross_arena_full_encoding() {
let parent = ArenaSlot::new(0, 100);
let child = ArenaSlot::new(1, 50);
let mut buf = Vec::new();
let len = encode_child_pointer(parent, child, &mut buf);
assert_eq!(len, CROSS_ARENA_SIZE);
assert_eq!(buf.len(), CROSS_ARENA_SIZE);
assert_eq!(buf[0], FLAG_CROSS_ARENA);
let (decoded, consumed) = decode_child_pointer(&buf, parent);
assert_eq!(consumed, CROSS_ARENA_SIZE);
assert_eq!(decoded.arena_id, 1);
assert_eq!(decoded.slot_id, 50);
}
#[test]
fn test_zero_delta() {
let parent = ArenaSlot::new(0, 100);
let child = ArenaSlot::new(0, 100);
let mut buf = Vec::new();
let len = encode_child_pointer(parent, child, &mut buf);
assert_eq!(len, 1);
assert_eq!(buf[0], 0);
let (decoded, _) = decode_child_pointer(&buf, parent);
assert_eq!(decoded.slot_id, 100);
}
#[test]
fn test_large_delta() {
let parent = ArenaSlot::new(0, 100000);
let child = ArenaSlot::new(0, 0);
let mut buf = Vec::new();
let len = encode_child_pointer(parent, child, &mut buf);
assert!(len > 1 && len < CROSS_ARENA_SIZE);
let (decoded, _) = decode_child_pointer(&buf, parent);
assert_eq!(decoded.arena_id, 0);
assert_eq!(decoded.slot_id, 0);
}
#[test]
fn test_encoded_size() {
let parent = ArenaSlot::new(0, 100);
let child_same = ArenaSlot::new(0, 95);
let child_cross = ArenaSlot::new(1, 50);
assert_eq!(encoded_size(parent, child_same), 1);
assert_eq!(encoded_size(parent, child_cross), CROSS_ARENA_SIZE);
}
#[test]
fn test_encode_decode_children() {
let parent = ArenaSlot::new(0, 100);
let children = vec![
ArenaSlot::new(0, 90), ArenaSlot::new(0, 80), ArenaSlot::new(1, 50), ];
let mut buf = Vec::new();
let total_len = encode_children(parent, &children, &mut buf);
let (decoded, consumed) = decode_children(&buf, parent, children.len());
assert_eq!(consumed, total_len);
assert_eq!(decoded.len(), 3);
assert_eq!(decoded[0], children[0]);
assert_eq!(decoded[1], children[1]);
assert_eq!(decoded[2], children[2]);
}
#[test]
fn test_sequential_siblings() {
let parent = ArenaSlot::new(0, 100);
let first_child = ArenaSlot::new(0, 80);
let count = 4;
let mut buf = Vec::new();
let len = encode_sequential_siblings(parent, first_child, &mut buf);
assert!(len < CROSS_ARENA_SIZE);
let (children, consumed) = decode_sequential_siblings(&buf, parent, count);
assert_eq!(consumed, len);
assert_eq!(children.len(), count);
assert_eq!(children[0], ArenaSlot::new(0, 80));
assert_eq!(children[1], ArenaSlot::new(0, 81));
assert_eq!(children[2], ArenaSlot::new(0, 82));
assert_eq!(children[3], ArenaSlot::new(0, 83));
}
#[test]
fn test_sequential_siblings_cross_arena() {
let parent = ArenaSlot::new(0, 100);
let first_child = ArenaSlot::new(1, 0); let count = 3;
let mut buf = Vec::new();
let len = encode_sequential_siblings(parent, first_child, &mut buf);
assert_eq!(len, CROSS_ARENA_SIZE);
let (children, _) = decode_sequential_siblings(&buf, parent, count);
assert_eq!(children.len(), count);
assert_eq!(children[0], ArenaSlot::new(1, 0));
assert_eq!(children[1], ArenaSlot::new(1, 1));
assert_eq!(children[2], ArenaSlot::new(1, 2));
}
#[test]
fn test_space_savings() {
let parent = ArenaSlot::new(0, 1000);
let children: Vec<ArenaSlot> = (990..1000).rev().map(|s| ArenaSlot::new(0, s)).collect();
let mut buf = Vec::new();
let relative_size = encode_children(parent, &children, &mut buf);
let fixed_size = children.len() * 8;
assert!(relative_size < fixed_size);
println!(
"Space savings: {} bytes relative vs {} bytes fixed ({:.1}% reduction)",
relative_size,
fixed_size,
(1.0 - relative_size as f64 / fixed_size as f64) * 100.0
);
}
}