use alloc::{
collections::{BTreeMap, VecDeque},
string::String,
vec::Vec,
};
#[cfg(feature = "std")]
use std::collections::HashMap;
use crate::{
canonical::{decode_varint_le, encode_varint_le, zigzag_decode, zigzag_encode},
config::{Config, TrailingBytes},
error::{Error, Result},
tags::{MARKER_U128, MARKER_U16, MARKER_U32, MARKER_U64},
writer::{CountWriter, EncodeWriter, SliceWriter},
};
pub trait CompactEncode {
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()>;
}
pub trait CompactDecode<'de>: Sized {
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CompactConfig {
base: Config,
}
impl CompactConfig {
pub(crate) const fn new(base: Config) -> Self {
Self { base }
}
pub const fn base_config(self) -> Config {
self.base
}
pub const fn profile(self) -> crate::BinaryProfile {
crate::BinaryProfile::CompactSchema
}
pub fn serialize<T: CompactEncode + ?Sized>(self, value: &T) -> Result<Vec<u8>> {
let mut output = Vec::new();
{
let mut writer = BoundedWriter::new(&mut output, self.base);
value.encode_compact(&mut writer)?;
}
Ok(output)
}
pub fn serialize_into_slice<T: CompactEncode + ?Sized>(
self,
output: &mut [u8],
value: &T,
) -> Result<usize> {
let mut writer = SliceWriter::new(output);
{
let mut bounded = BoundedWriter::new(&mut writer, self.base);
value.encode_compact(&mut bounded)?;
}
writer.finish()
}
pub fn serialized_size<T: CompactEncode + ?Sized>(self, value: &T) -> Result<u64> {
let mut writer = CountWriter::new();
value.encode_compact(&mut writer)?;
Ok(writer.written())
}
pub fn deserialize<'de, T: CompactDecode<'de>>(self, input: &'de [u8]) -> Result<T> {
let mut cursor = CompactCursor::new(input, self.base);
let value = T::decode_compact(&mut cursor)?;
if self.base.trailing == TrailingBytes::Reject && cursor.position() != input.len() {
return Err(Error::TrailingBytes {
remaining: input.len() - cursor.position(),
});
}
Ok(value)
}
}
struct BoundedWriter<W> {
inner: W,
limit: Option<u64>,
depth_limit: usize,
written: u64,
depth: usize,
}
impl<W: EncodeWriter> BoundedWriter<W> {
fn new(inner: W, config: Config) -> Self {
Self {
inner,
limit: config.limit,
depth_limit: config.depth_limit,
written: 0,
depth: 0,
}
}
}
impl<W: EncodeWriter> EncodeWriter for BoundedWriter<W> {
fn write_all(&mut self, bytes: &[u8]) -> Result<()> {
let amount =
u64::try_from(bytes.len()).map_err(|_| Error::IntegerOverflow { target: "u64" })?;
let next = self
.written
.checked_add(amount)
.ok_or(Error::SizeLimit { limit: u64::MAX })?;
if let Some(limit) = self.limit {
if next > limit {
return Err(Error::SizeLimit { limit });
}
}
self.inner.write_all(bytes)?;
self.written = next;
Ok(())
}
fn enter_container(&mut self) -> Result<()> {
if self.depth >= self.depth_limit {
return Err(Error::Custom(
"compact encode nesting depth limit exceeded".into(),
));
}
self.depth += 1;
Ok(())
}
fn exit_container(&mut self) {
debug_assert!(self.depth > 0);
self.depth -= 1;
}
}
#[doc(hidden)]
pub fn __type_equal<Src: ?Sized, Target: ?Sized>() -> bool {
non_static_type_id::<Src>() == non_static_type_id::<Target>()
}
fn non_static_type_id<T: ?Sized>() -> core::any::TypeId {
trait NonStaticAny {
fn get_type_id(&self) -> core::any::TypeId
where
Self: 'static;
}
impl<T: ?Sized> NonStaticAny for core::marker::PhantomData<T> {
fn get_type_id(&self) -> core::any::TypeId
where
Self: 'static,
{
core::any::TypeId::of::<T>()
}
}
let phantom = core::marker::PhantomData::<T>;
unsafe {
core::mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom)
.get_type_id()
}
}
#[inline]
pub(crate) fn encode_varint<W: EncodeWriter + ?Sized>(writer: &mut W, value: u64) -> Result<()> {
let (bytes, len) = encode_varint_le(value as u128);
writer.write_all(&bytes[..len])
}
#[inline]
pub(crate) fn encode_varint_u128<W: EncodeWriter + ?Sized>(
writer: &mut W,
value: u128,
) -> Result<()> {
let (bytes, len) = encode_varint_le(value);
writer.write_all(&bytes[..len])
}
#[inline]
pub(crate) fn encode_zigzag<W: EncodeWriter + ?Sized>(writer: &mut W, value: i64) -> Result<()> {
encode_varint_u128(writer, zigzag_encode(value as i128))
}
#[inline]
pub(crate) fn encode_bool<W: EncodeWriter + ?Sized>(writer: &mut W, value: bool) -> Result<()> {
writer.write_all(&[value as u8])
}
#[inline]
pub(crate) fn encode_length<W: EncodeWriter + ?Sized>(writer: &mut W, len: usize) -> Result<()> {
let len = u64::try_from(len).map_err(|_| Error::IntegerOverflow { target: "u64" })?;
encode_varint(writer, len)
}
#[inline]
pub(crate) fn encode_bytes<W: EncodeWriter + ?Sized>(writer: &mut W, bytes: &[u8]) -> Result<()> {
encode_length(writer, bytes.len())?;
writer.write_all(bytes)
}
pub(crate) fn encode_slice<W: EncodeWriter + ?Sized, T: CompactEncode>(
writer: &mut W,
values: &[T],
) -> Result<()> {
encode_length(writer, values.len())?;
writer.enter_container()?;
let outcome = encode_elements(writer, values);
writer.exit_container();
outcome
}
fn encode_elements<W: EncodeWriter + ?Sized, T: CompactEncode>(
writer: &mut W,
values: &[T],
) -> Result<()> {
if __type_equal::<T, u8>() || __type_equal::<T, i8>() {
let bytes =
unsafe { core::slice::from_raw_parts(values.as_ptr().cast::<u8>(), values.len()) };
return writer.write_all(bytes);
}
if __type_equal::<T, f64>() {
let values: &[f64] = unsafe { core::mem::transmute(values) };
return write_f64_slice_le(writer, values);
}
if __type_equal::<T, f32>() {
let values: &[f32] = unsafe { core::mem::transmute(values) };
return write_f32_slice_le(writer, values);
}
if __type_equal::<T, u64>() {
let values: &[u64] = unsafe { core::mem::transmute(values) };
return encode_varints_bulk(writer, values.iter().copied());
}
if __type_equal::<T, i64>() {
let values: &[i64] = unsafe { core::mem::transmute(values) };
return encode_varints_bulk(
writer,
values.iter().map(|&v| zigzag_encode(v as i128) as u64),
);
}
if __type_equal::<T, u32>() {
let values: &[u32] = unsafe { core::mem::transmute(values) };
return encode_varints_bulk(writer, values.iter().map(|&v| v as u64));
}
if __type_equal::<T, i32>() {
let values: &[i32] = unsafe { core::mem::transmute(values) };
return encode_varints_bulk(
writer,
values.iter().map(|&v| zigzag_encode(v as i128) as u64),
);
}
if __type_equal::<T, u16>() {
let values: &[u16] = unsafe { core::mem::transmute(values) };
return encode_varints_bulk(writer, values.iter().map(|&v| v as u64));
}
if __type_equal::<T, i16>() {
let values: &[i16] = unsafe { core::mem::transmute(values) };
return encode_varints_bulk(
writer,
values.iter().map(|&v| zigzag_encode(v as i128) as u64),
);
}
for value in values {
value.encode_compact(writer)?;
}
Ok(())
}
fn encode_varints_bulk<W: EncodeWriter + ?Sized>(
writer: &mut W,
values: impl Iterator<Item = u64>,
) -> Result<()> {
let mut buffer = [0u8; 64];
let mut used = 0usize;
for value in values {
if used + 17 > buffer.len() {
writer.write_all(&buffer[..used])?;
used = 0;
}
used += write_varint_into(&mut buffer[used..], value);
}
if used > 0 {
writer.write_all(&buffer[..used])?;
}
Ok(())
}
fn write_f64_slice_le<W: EncodeWriter + ?Sized>(writer: &mut W, values: &[f64]) -> Result<()> {
if cfg!(target_endian = "little") {
let byte_len = values
.len()
.checked_mul(core::mem::size_of::<f64>())
.ok_or(Error::IntegerOverflow { target: "usize" })?;
let bytes = unsafe { core::slice::from_raw_parts(values.as_ptr().cast::<u8>(), byte_len) };
writer.write_all(bytes)
} else {
for value in values {
writer.write_all(&value.to_le_bytes())?;
}
Ok(())
}
}
fn write_f32_slice_le<W: EncodeWriter + ?Sized>(writer: &mut W, values: &[f32]) -> Result<()> {
if cfg!(target_endian = "little") {
let byte_len = values
.len()
.checked_mul(core::mem::size_of::<f32>())
.ok_or(Error::IntegerOverflow { target: "usize" })?;
let bytes = unsafe { core::slice::from_raw_parts(values.as_ptr().cast::<u8>(), byte_len) };
writer.write_all(bytes)
} else {
for value in values {
writer.write_all(&value.to_le_bytes())?;
}
Ok(())
}
}
fn encode_zigzag_i128<W: EncodeWriter + ?Sized>(writer: &mut W, value: i128) -> Result<()> {
encode_varint_u128(writer, zigzag_encode(value))
}
fn plain_varint_prefix(input: &[u8]) -> usize {
#[cfg(feature = "simd")]
return crate::simd::plain_varint_prefix(input);
#[cfg(not(feature = "simd"))]
input.iter().take_while(|&&byte| byte <= 250).count()
}
#[inline]
fn write_varint_into(buffer: &mut [u8], value: u64) -> usize {
if value <= 250 {
buffer[0] = value as u8;
1
} else if value <= 0xffff {
buffer[0] = MARKER_U16;
buffer[1..3].copy_from_slice(&(value as u16).to_le_bytes());
3
} else if value <= 0xffff_ffff {
buffer[0] = MARKER_U32;
buffer[1..5].copy_from_slice(&(value as u32).to_le_bytes());
5
} else {
buffer[0] = MARKER_U64;
buffer[1..9].copy_from_slice(&value.to_le_bytes());
9
}
}
pub struct CompactCursor<'de> {
input: &'de [u8],
position: usize,
limit_end: Option<usize>,
collection_limit: Option<u64>,
depth_limit: usize,
depth: usize,
}
impl<'de> CompactCursor<'de> {
pub(crate) fn new(input: &'de [u8], config: Config) -> Self {
let limit_end = config.limit.and_then(|limit| {
usize::try_from(limit)
.ok()
.filter(|&limit| input.len() > limit)
});
Self {
input,
position: 0,
limit_end,
collection_limit: config.collection_limit,
depth_limit: config.depth_limit,
depth: 0,
}
}
#[inline(always)]
fn enforce_limit(&self, end: usize) -> Result<()> {
if let Some(end_limit) = self.limit_end {
if end > end_limit {
return Err(Error::SizeLimit {
limit: end_limit as u64,
});
}
}
Ok(())
}
pub const fn position(&self) -> usize {
self.position
}
pub const fn remaining(&self) -> usize {
self.input.len() - self.position
}
#[inline]
fn take(&mut self, len: usize) -> Result<&'de [u8]> {
let end = self.position.checked_add(len).ok_or(Error::UnexpectedEnd)?;
self.enforce_limit(end)?;
let bytes = self
.input
.get(self.position..end)
.ok_or(Error::UnexpectedEnd)?;
self.position = end;
Ok(bytes)
}
#[inline]
fn advance_one(&mut self, position: usize) -> Result<()> {
let end = position + 1;
self.enforce_limit(end)?;
self.position = end;
Ok(())
}
#[inline]
pub(crate) fn byte(&mut self) -> Result<u8> {
let position = self.position;
match self.input.get(position) {
Some(&byte) => {
self.advance_one(position)?;
Ok(byte)
}
None => Err(Error::UnexpectedEnd),
}
}
#[inline]
pub(crate) fn read_varint(&mut self) -> Result<u64> {
let position = self.position;
match self.input.get(position) {
None => Err(Error::UnexpectedEnd),
Some(&marker) if marker <= 250 => {
self.advance_one(position)?;
Ok(marker as u64)
}
Some(&marker) => {
let end = position
+ match marker {
MARKER_U16 => 3,
MARKER_U32 => 5,
MARKER_U64 => 9,
MARKER_U128 => 17,
other => return Err(Error::InvalidVarintMarker(other)),
};
self.enforce_limit(end)?;
let bytes = self
.input
.get(position + 1..end)
.ok_or(Error::UnexpectedEnd)?;
self.position = end;
let value = decode_varint_le(marker, bytes).ok_or(Error::NonCanonicalVarint)?;
u64::try_from(value).map_err(|_| Error::IntegerOverflow { target: "u64" })
}
}
}
#[inline]
pub(crate) fn read_varint_u128(&mut self) -> Result<u128> {
let position = self.position;
match self.input.get(position) {
None => Err(Error::UnexpectedEnd),
Some(&marker) if marker <= 250 => {
self.advance_one(position)?;
Ok(marker as u128)
}
Some(&marker) => {
let end = position
+ match marker {
MARKER_U16 => 3,
MARKER_U32 => 5,
MARKER_U64 => 9,
MARKER_U128 => 17,
other => return Err(Error::InvalidVarintMarker(other)),
};
self.enforce_limit(end)?;
let bytes = self
.input
.get(position + 1..end)
.ok_or(Error::UnexpectedEnd)?;
self.position = end;
decode_varint_le(marker, bytes).ok_or(Error::NonCanonicalVarint)
}
}
}
#[inline]
pub(crate) fn read_zigzag(&mut self) -> Result<i64> {
let encoded = self.read_varint_u128()?;
let value = zigzag_decode(encoded);
i64::try_from(value).map_err(|_| Error::IntegerOverflow { target: "i64" })
}
#[inline]
pub(crate) fn read_zigzag_i128(&mut self) -> Result<i128> {
Ok(zigzag_decode(self.read_varint_u128()?))
}
#[inline]
pub(crate) fn read_bool(&mut self) -> Result<bool> {
match self.byte()? {
0 => Ok(false),
1 => Ok(true),
other => Err(Error::InvalidBool(other)),
}
}
#[inline]
pub(crate) fn read_f32(&mut self) -> Result<f32> {
let bytes = self.take(4)?;
Ok(f32::from_le_bytes(bytes.try_into().expect("fixed width")))
}
#[inline]
pub(crate) fn read_f64(&mut self) -> Result<f64> {
let bytes = self.take(8)?;
Ok(f64::from_le_bytes(bytes.try_into().expect("fixed width")))
}
#[inline]
pub(crate) fn read_length(&mut self) -> Result<usize> {
let value = self.read_varint()?;
usize::try_from(value).map_err(|_| Error::IntegerOverflow { target: "usize" })
}
#[inline]
pub(crate) fn read_bytes(&mut self, len: usize) -> Result<&'de [u8]> {
self.take(len)
}
#[inline]
pub(crate) fn read_str(&mut self) -> Result<&'de str> {
let len = self.read_length()?;
let bytes = self.take(len)?;
core::str::from_utf8(bytes).map_err(Error::InvalidUtf8)
}
#[inline]
pub(crate) fn enter_container(&mut self, len: usize) -> Result<()> {
if let Some(limit) = self.collection_limit {
if len as u64 > limit {
return Err(Error::CollectionLimit { limit });
}
}
if self.depth >= self.depth_limit {
return Err(Error::Custom(
"compact decode nesting depth limit exceeded".into(),
));
}
self.depth += 1;
Ok(())
}
pub(crate) fn read_varints_bulk(&mut self, len: usize) -> Result<Vec<u64>> {
let mut out = Vec::new();
out.try_reserve_exact(len)
.map_err(|_| Error::SizeLimit { limit: u64::MAX })?;
let mut remaining = len;
while remaining > 0 {
let position = self.position;
match self.input.get(position) {
None => return Err(Error::UnexpectedEnd),
Some(&marker) if marker <= 250 => {
let available = &self.input[position + 1..];
let window = &available[..(remaining - 1).min(available.len())];
let extra = plain_varint_prefix(window).min(remaining - 1);
let end = position + 1 + extra;
self.enforce_limit(end)?;
for &byte in &self.input[position..end] {
out.push(byte as u64);
}
self.position = end;
remaining -= 1 + extra;
}
Some(&marker) => {
let end = position
+ match marker {
MARKER_U16 => 3,
MARKER_U32 => 5,
MARKER_U64 => 9,
MARKER_U128 => 17,
other => return Err(Error::InvalidVarintMarker(other)),
};
self.enforce_limit(end)?;
let bytes = self
.input
.get(position + 1..end)
.ok_or(Error::UnexpectedEnd)?;
let value = decode_varint_le(marker, bytes).ok_or(Error::NonCanonicalVarint)?;
out.push(
u64::try_from(value)
.map_err(|_| Error::IntegerOverflow { target: "u64" })?,
);
self.position = end;
remaining -= 1;
}
}
}
Ok(out)
}
#[inline]
pub(crate) fn exit_container(&mut self) {
debug_assert!(self.depth > 0);
self.depth -= 1;
}
}
fn read_f64_vec(cursor: &mut CompactCursor<'_>, len: usize) -> Result<Vec<f64>> {
let byte_len = len
.checked_mul(core::mem::size_of::<f64>())
.ok_or(Error::IntegerOverflow { target: "usize" })?;
let bytes = cursor.read_bytes(byte_len)?;
let mut out = Vec::new();
out.try_reserve_exact(len)
.map_err(|_| Error::SizeLimit { limit: u64::MAX })?;
if cfg!(target_endian = "little")
&& (bytes.as_ptr() as usize).is_multiple_of(core::mem::align_of::<f64>())
{
let values = unsafe { core::slice::from_raw_parts(bytes.as_ptr().cast::<f64>(), len) };
out.copy_from_slice(values);
} else {
for chunk in bytes.chunks_exact(core::mem::size_of::<f64>()) {
out.push(f64::from_le_bytes(
chunk.try_into().expect("chunks_exact yields 8 bytes"),
));
}
}
Ok(out)
}
fn read_f32_vec(cursor: &mut CompactCursor<'_>, len: usize) -> Result<Vec<f32>> {
let byte_len = len
.checked_mul(core::mem::size_of::<f32>())
.ok_or(Error::IntegerOverflow { target: "usize" })?;
let bytes = cursor.read_bytes(byte_len)?;
let mut out = Vec::new();
out.try_reserve_exact(len)
.map_err(|_| Error::SizeLimit { limit: u64::MAX })?;
if cfg!(target_endian = "little")
&& (bytes.as_ptr() as usize).is_multiple_of(core::mem::align_of::<f32>())
{
let values = unsafe { core::slice::from_raw_parts(bytes.as_ptr().cast::<f32>(), len) };
out.copy_from_slice(values);
} else {
for chunk in bytes.chunks_exact(core::mem::size_of::<f32>()) {
out.push(f32::from_le_bytes(
chunk.try_into().expect("chunks_exact yields 4 bytes"),
));
}
}
Ok(out)
}
#[doc(hidden)]
pub fn encode_variant_index<W: EncodeWriter + ?Sized>(writer: &mut W, index: u32) -> Result<()> {
encode_varint(writer, index as u64)
}
#[doc(hidden)]
pub fn decode_variant_index(cursor: &mut CompactCursor<'_>) -> Result<u32> {
let index = cursor.read_varint()?;
u32::try_from(index).map_err(|_| Error::IntegerOverflow { target: "u32" })
}
#[doc(hidden)]
pub fn __err_static(message: &'static str) -> Error {
Error::Custom(crate::error::__custom_message(message))
}
impl CompactEncode for () {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, _writer: &mut W) -> Result<()> {
Ok(())
}
}
impl<'de> CompactDecode<'de> for () {
#[inline]
fn decode_compact(_cursor: &mut CompactCursor<'de>) -> Result<Self> {
Ok(())
}
}
impl CompactEncode for bool {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_bool(writer, *self)
}
}
impl<'de> CompactDecode<'de> for bool {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
cursor.read_bool()
}
}
impl CompactEncode for u8 {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
writer.write_all(&[*self])
}
}
impl<'de> CompactDecode<'de> for u8 {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
cursor.byte()
}
}
impl CompactEncode for i8 {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
writer.write_all(&[*self as u8])
}
}
impl<'de> CompactDecode<'de> for i8 {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
Ok(cursor.byte()? as i8)
}
}
macro_rules! impl_unsigned_varint {
($($ty:ty => $name:literal),+ $(,)?) => {$(
impl CompactEncode for $ty {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_varint(writer, *self as u64)
}
}
impl<'de> CompactDecode<'de> for $ty {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
let value = cursor.read_varint()?;
<$ty>::try_from(value)
.map_err(|_| Error::IntegerOverflow { target: $name })
}
}
)+};
}
impl_unsigned_varint!(u16 => "u16", u32 => "u32", u64 => "u64");
impl CompactEncode for u128 {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_varint_u128(writer, *self)
}
}
impl<'de> CompactDecode<'de> for u128 {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
cursor.read_varint_u128()
}
}
macro_rules! impl_signed_zigzag {
($($ty:ty => $name:literal),+ $(,)?) => {$(
impl CompactEncode for $ty {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_zigzag(writer, *self as i64)
}
}
impl<'de> CompactDecode<'de> for $ty {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
let value = cursor.read_zigzag()?;
<$ty>::try_from(value)
.map_err(|_| Error::IntegerOverflow { target: $name })
}
}
)+};
}
impl_signed_zigzag!(i16 => "i16", i32 => "i32", i64 => "i64");
impl CompactEncode for i128 {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_zigzag_i128(writer, *self)
}
}
impl<'de> CompactDecode<'de> for i128 {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
cursor.read_zigzag_i128()
}
}
impl CompactEncode for f32 {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
writer.write_all(&self.to_le_bytes())
}
}
impl<'de> CompactDecode<'de> for f32 {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
cursor.read_f32()
}
}
impl CompactEncode for f64 {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
writer.write_all(&self.to_le_bytes())
}
}
impl<'de> CompactDecode<'de> for f64 {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
cursor.read_f64()
}
}
impl CompactEncode for char {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_varint(writer, *self as u32 as u64)
}
}
impl<'de> CompactDecode<'de> for char {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
let value = u32::try_from(cursor.read_varint()?)
.map_err(|_| Error::IntegerOverflow { target: "u32" })?;
char::from_u32(value).ok_or(Error::InvalidChar)
}
}
impl CompactEncode for str {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_bytes(writer, self.as_bytes())
}
}
impl CompactEncode for &str {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_bytes(writer, self.as_bytes())
}
}
impl CompactEncode for String {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_bytes(writer, self.as_bytes())
}
}
impl<'de, 'a> CompactDecode<'de> for &'a str
where
'de: 'a,
{
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<&'a str> {
cursor.read_str()
}
}
impl<'de> CompactDecode<'de> for String {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<String> {
Ok(cursor.read_str()?.into())
}
}
impl<'de, 'a> CompactDecode<'de> for &'a [u8]
where
'de: 'a,
{
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<&'a [u8]> {
let len = cursor.read_length()?;
cursor.read_bytes(len)
}
}
impl<T: CompactEncode> CompactEncode for [T] {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_slice(writer, self)
}
}
impl<T: CompactEncode> CompactEncode for Vec<T> {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_slice(writer, self.as_slice())
}
}
impl<T: CompactEncode> CompactEncode for &[T] {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_slice(writer, self)
}
}
impl<'de, T: CompactDecode<'de>> CompactDecode<'de> for Vec<T> {
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Vec<T>> {
let len = cursor.read_length()?;
cursor.enter_container(len)?;
let outcome: Result<Vec<T>> = (|| {
if __type_equal::<T, u8>() {
let bytes = cursor.read_bytes(len)?;
let mut out = Vec::new();
out.try_reserve_exact(len)
.map_err(|_| Error::SizeLimit { limit: u64::MAX })?;
out.extend_from_slice(bytes);
return Ok(unsafe { core::mem::transmute::<Vec<u8>, Vec<T>>(out) });
}
if __type_equal::<T, i8>() {
let bytes = cursor.read_bytes(len)?;
let values = unsafe {
core::slice::from_raw_parts(bytes.as_ptr().cast::<i8>(), bytes.len())
};
let mut out = Vec::new();
out.try_reserve_exact(len)
.map_err(|_| Error::SizeLimit { limit: u64::MAX })?;
out.copy_from_slice(values);
return Ok(unsafe { core::mem::transmute::<Vec<i8>, Vec<T>>(out) });
}
if __type_equal::<T, f64>() {
let values = read_f64_vec(cursor, len)?;
return Ok(unsafe { core::mem::transmute::<Vec<f64>, Vec<T>>(values) });
}
if __type_equal::<T, f32>() {
let values = read_f32_vec(cursor, len)?;
return Ok(unsafe { core::mem::transmute::<Vec<f32>, Vec<T>>(values) });
}
if __type_equal::<T, u64>() {
let values = cursor.read_varints_bulk(len)?;
return Ok(unsafe { core::mem::transmute::<Vec<u64>, Vec<T>>(values) });
}
let mut out = Vec::new();
out.try_reserve_exact(len)
.map_err(|_| Error::SizeLimit { limit: u64::MAX })?;
for _ in 0..len {
out.push(T::decode_compact(cursor)?);
}
Ok(out)
})();
cursor.exit_container();
outcome
}
}
impl<T: CompactEncode, const N: usize> CompactEncode for [T; N] {
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_elements(writer, self)
}
}
impl<'de, T: CompactDecode<'de>, const N: usize> CompactDecode<'de> for [T; N] {
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<[T; N]> {
let mut slots: [core::mem::MaybeUninit<T>; N] =
core::array::from_fn(|_| core::mem::MaybeUninit::uninit());
for slot in &mut slots {
slot.write(T::decode_compact(cursor)?);
}
Ok(unsafe { core::mem::transmute_copy(&slots) })
}
}
impl<T: CompactEncode> CompactEncode for Option<T> {
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
match self {
None => encode_bool(writer, false),
Some(value) => {
encode_bool(writer, true)?;
value.encode_compact(writer)
}
}
}
}
impl<'de, T: CompactDecode<'de>> CompactDecode<'de> for Option<T> {
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Option<T>> {
if cursor.read_bool()? {
Ok(Some(T::decode_compact(cursor)?))
} else {
Ok(None)
}
}
}
impl<T: CompactEncode> CompactEncode for VecDeque<T> {
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_length(writer, self.len())?;
writer.enter_container()?;
let outcome = (|| {
let (head, tail) = self.as_slices();
encode_elements(writer, head)?;
encode_elements(writer, tail)
})();
writer.exit_container();
outcome
}
}
impl<'de, T: CompactDecode<'de>> CompactDecode<'de> for VecDeque<T> {
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<VecDeque<T>> {
let len = cursor.read_length()?;
cursor.enter_container(len)?;
let outcome = (|| {
let mut out = VecDeque::new();
out.try_reserve(len)
.map_err(|_| Error::SizeLimit { limit: u64::MAX })?;
for _ in 0..len {
out.push_back(T::decode_compact(cursor)?);
}
Ok(out)
})();
cursor.exit_container();
outcome
}
}
impl<K: CompactEncode, V: CompactEncode> CompactEncode for BTreeMap<K, V> {
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_length(writer, self.len())?;
writer.enter_container()?;
let outcome = (|| {
for (key, value) in self {
key.encode_compact(writer)?;
value.encode_compact(writer)?;
}
Ok(())
})();
writer.exit_container();
outcome
}
}
impl<'de, K: CompactDecode<'de> + Ord, V: CompactDecode<'de>> CompactDecode<'de>
for BTreeMap<K, V>
{
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<BTreeMap<K, V>> {
let len = cursor.read_length()?;
cursor.enter_container(len)?;
let outcome = (|| {
let mut out = BTreeMap::new();
for _ in 0..len {
let key = K::decode_compact(cursor)?;
let value = V::decode_compact(cursor)?;
if out.insert(key, value).is_some() {
return Err(Error::Custom("duplicate map key".into()));
}
}
Ok(out)
})();
cursor.exit_container();
outcome
}
}
#[cfg(feature = "std")]
impl<K: CompactEncode, V: CompactEncode> CompactEncode for HashMap<K, V> {
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
encode_length(writer, self.len())?;
writer.enter_container()?;
let outcome = (|| {
for (key, value) in self {
key.encode_compact(writer)?;
value.encode_compact(writer)?;
}
Ok(())
})();
writer.exit_container();
outcome
}
}
#[cfg(feature = "std")]
impl<'de, K: CompactDecode<'de> + Eq + core::hash::Hash, V: CompactDecode<'de>> CompactDecode<'de>
for HashMap<K, V>
{
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<HashMap<K, V>> {
let len = cursor.read_length()?;
cursor.enter_container(len)?;
let outcome = (|| {
let mut out = HashMap::new();
out.try_reserve(len)
.map_err(|_| Error::SizeLimit { limit: u64::MAX })?;
for _ in 0..len {
let key = K::decode_compact(cursor)?;
let value = V::decode_compact(cursor)?;
if out.insert(key, value).is_some() {
return Err(Error::Custom("duplicate map key".into()));
}
}
Ok(out)
})();
cursor.exit_container();
outcome
}
}
macro_rules! impl_tuple {
($($name:ident),+ $(,)?) => {
impl<$($name: CompactEncode),+> CompactEncode for ($($name,)+) {
#[allow(non_snake_case)]
#[inline]
fn encode_compact<W: EncodeWriter + ?Sized>(&self, writer: &mut W) -> Result<()> {
let ($($name,)+) = self;
$( $name.encode_compact(writer)?; )+
Ok(())
}
}
impl<'de, $($name: CompactDecode<'de>),+> CompactDecode<'de> for ($($name,)+) {
#[allow(non_snake_case)]
#[inline]
fn decode_compact(cursor: &mut CompactCursor<'de>) -> Result<Self> {
Ok(($( $name::decode_compact(cursor)?, )+))
}
}
};
}
impl_tuple!(A);
impl_tuple!(A, B);
impl_tuple!(A, B, C);
impl_tuple!(A, B, C, D);
impl_tuple!(A, B, C, D, E);
impl_tuple!(A, B, C, D, E, F);
impl_tuple!(A, B, C, D, E, F, G);
impl_tuple!(A, B, C, D, E, F, G, H);
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
use alloc::vec;
fn roundtrip<T>(value: &T) -> Vec<u8>
where
T: CompactEncode + for<'de> CompactDecode<'de> + PartialEq + core::fmt::Debug,
{
let config = Config::standard();
let compact = CompactConfig::new(config);
let bytes = compact.serialize(value).unwrap();
let decoded: T = compact.deserialize(&bytes).unwrap();
assert_eq!(decoded, *value);
bytes
}
#[test]
fn inline_varint_write_matches_canonical() {
let cases = [
0u64,
1,
250,
251,
0xffff,
0x1_0000,
0xffff_ffff,
0x1_0000_0000,
u64::MAX,
];
for value in cases {
let mut buffer = [0u8; 32];
let len = write_varint_into(&mut buffer, value);
let (canonical, canonical_len) = crate::canonical::encode_varint_le(value as u128);
assert_eq!(&buffer[..len], &canonical[..canonical_len], "value {value}");
assert_eq!(len, canonical_len);
}
}
#[test]
fn bulk_paths_agree_with_single_value_paths() {
let mut values = Vec::new();
values.extend(0..300); values.push(251);
values.push(0xffff);
values.push(0x1_0000);
values.push(0xffff_ffff);
values.push(0x1_0000_0000);
values.push(u64::MAX);
values.extend((0..300).map(|i| 0x1_0000 + i));
let config = Config::standard();
let compact = CompactConfig::new(config);
let bytes = compact.serialize(&values).unwrap();
assert_eq!(compact.deserialize::<Vec<u64>>(&bytes).unwrap(), values);
}
#[test]
fn primitives_roundtrip() {
roundtrip(&true);
roundtrip(&false);
roundtrip(&0u8);
roundtrip(&250u8);
roundtrip(&251u8);
roundtrip(&255u8);
roundtrip(&u16::MAX);
roundtrip(&u32::MAX);
roundtrip(&u64::MAX);
roundtrip(&u128::MAX);
roundtrip(&i8::MIN);
roundtrip(&i16::MIN);
roundtrip(&i32::MIN);
roundtrip(&i64::MIN);
roundtrip(&i128::MIN);
roundtrip(&-1i64);
roundtrip(&0i64);
roundtrip(&f32::MIN);
roundtrip(&f32::MAX);
roundtrip(&f64::MIN);
roundtrip(&f64::MAX);
roundtrip(&-0.0f64);
roundtrip(&'a');
roundtrip(&'中');
roundtrip(&());
}
#[test]
fn u8_is_single_raw_byte() {
let bytes = CompactConfig::new(Config::standard())
.serialize(&255u8)
.unwrap();
assert_eq!(bytes, &[255]);
let bytes = CompactConfig::new(Config::standard())
.serialize(&0u8)
.unwrap();
assert_eq!(bytes, &[0]);
}
#[test]
fn string_and_bytes_roundtrip() {
let bytes = roundtrip(&"hello".to_string());
assert_eq!(bytes, &[5, b'h', b'e', b'l', b'l', b'o']);
let config = CompactConfig::new(Config::standard());
let encoded = config.serialize(&b"raw bytes".as_slice()).unwrap();
let decoded: &[u8] = config.deserialize(&encoded).unwrap();
assert_eq!(decoded, b"raw bytes");
let encoded = config.serialize(&"borrowed").unwrap();
let decoded: &str = config.deserialize(&encoded).unwrap();
assert_eq!(decoded, "borrowed");
roundtrip(&Vec::from(b"raw bytes".as_slice()));
}
#[test]
fn byte_vec_is_length_plus_memcpy() {
let config = CompactConfig::new(Config::standard());
let value = vec![0u8, 251, 252, 255, 7, 0, 128];
let bytes = config.serialize(&value).unwrap();
assert_eq!(bytes, &[7, 0, 251, 252, 255, 7, 0, 128]);
let decoded: Vec<u8> = config.deserialize(&bytes).unwrap();
assert_eq!(decoded, value);
}
#[test]
fn option_roundtrip() {
let config = CompactConfig::new(Config::standard());
let none: Option<u64> = None;
let none_bytes = config.serialize(&none).unwrap();
assert_eq!(none_bytes, &[0]);
assert_eq!(
config.deserialize::<Option<u64>>(&none_bytes).unwrap(),
None
);
let some_bytes = config.serialize(&Some(7u64)).unwrap();
assert_eq!(some_bytes, &[1, 7]);
assert_eq!(
config.deserialize::<Option<u64>>(&some_bytes).unwrap(),
Some(7)
);
}
#[test]
fn numeric_vecs_roundtrip() {
roundtrip(&(0..300u16).collect::<Vec<_>>());
roundtrip(&(0..300u32).collect::<Vec<_>>());
roundtrip(&(0..300u64).collect::<Vec<_>>());
roundtrip(&(-150..150i16).collect::<Vec<_>>());
roundtrip(&(-150..150i32).collect::<Vec<_>>());
roundtrip(&(-150..150i64).collect::<Vec<_>>());
roundtrip(&(0..100).map(|i| i as f32 * 0.5).collect::<Vec<_>>());
roundtrip(&(0..100).map(|i| i as f64 * 0.25).collect::<Vec<_>>());
}
#[test]
fn vec_encodes_with_single_length_prefix() {
let config = CompactConfig::new(Config::standard());
let values = vec![1u64, 2, 3, 4];
let bytes = config.serialize(&values).unwrap();
assert_eq!(bytes, &[4, 1, 2, 3, 4]);
}
#[test]
fn float_vec_is_compact_payload() {
let config = CompactConfig::new(Config::standard());
let values = vec![1.0f64, -2.0, 3.5];
let bytes = config.serialize(&values).unwrap();
assert_eq!(bytes.len(), 1 + 3 * 8);
let decoded: Vec<f64> = config.deserialize(&bytes).unwrap();
assert_eq!(decoded, values);
}
#[test]
fn arrays_and_tuples_roundtrip() {
roundtrip(&[1u64, 2, 3, 4]);
roundtrip(&[0u8, 255, 1, 2]);
roundtrip(&(true, 1i16, 2u64, 3.0f64, 'x', ()));
let config = CompactConfig::new(Config::standard());
let value = (7u32, "seven", 1.5f32);
let bytes = config.serialize(&value).unwrap();
let decoded: (u32, &str, f32) = config.deserialize(&bytes).unwrap();
assert_eq!(decoded, value);
}
#[test]
fn maps_roundtrip() {
let config = CompactConfig::new(Config::standard());
let mut map = BTreeMap::new();
map.insert(3u32, "three");
map.insert(1u32, "one");
let bytes = config.serialize(&map).unwrap();
let decoded: BTreeMap<u32, &str> = config.deserialize(&bytes).unwrap();
assert_eq!(decoded, map);
assert_eq!(
bytes,
&[2, 1, 3, b'o', b'n', b'e', 3, 5, b't', b'h', b'r', b'e', b'e']
);
}
#[test]
fn nested_containers_roundtrip() {
roundtrip(&vec![vec![1u8, 2], vec![3, 4, 5]]);
roundtrip(&vec![Some(1u64), None, Some(3)]);
roundtrip(&vec!["a".to_string(), "bb".to_string(), "ccc".to_string()]);
}
#[test]
fn collections_are_bounded_by_limits() {
let config = CompactConfig::new(Config::standard().with_collection_limit(2));
let encoded = config.serialize(&vec![1u64, 2, 3]).unwrap();
let error = config.deserialize::<Vec<u64>>(&encoded).unwrap_err();
assert!(matches!(error, Error::CollectionLimit { limit: 2 }));
let encoded = CompactConfig::new(Config::standard())
.serialize(&vec![1u64, 2])
.unwrap();
let config = CompactConfig::new(Config::standard().with_limit(2));
let error = config.deserialize::<Vec<u64>>(&encoded).unwrap_err();
assert!(matches!(error, Error::SizeLimit { limit: 2 }));
}
#[test]
fn depth_limit_rejects_deep_nesting() {
let mut cursor = CompactCursor::new(&[0u8; 1], Config::standard().with_depth_limit(4));
for _ in 0..4 {
cursor.enter_container(0).unwrap();
}
let error = cursor.enter_container(0).unwrap_err();
assert!(matches!(error, Error::Custom(_)));
}
#[test]
fn trailing_bytes_are_rejected() {
let config = CompactConfig::new(Config::standard());
let mut encoded = config.serialize(&1u64).unwrap();
encoded.push(0xff);
let error = config.deserialize::<u64>(&encoded).unwrap_err();
assert!(matches!(error, Error::TrailingBytes { remaining: 1 }));
}
#[test]
fn truncated_input_fails_cleanly() {
let config = CompactConfig::new(Config::standard());
let encoded = config.serialize(&"hello").unwrap();
for cut in 0..encoded.len() {
let error = config.deserialize::<String>(&encoded[..cut]).unwrap_err();
assert!(matches!(
error,
Error::UnexpectedEnd | Error::InvalidUtf8(_)
));
}
}
#[test]
fn non_canonical_varints_are_rejected() {
let config = CompactConfig::new(Config::standard());
let error = config.deserialize::<u64>(&[MARKER_U16, 5, 0]).unwrap_err();
assert!(matches!(error, Error::NonCanonicalVarint));
}
#[test]
fn invalid_bool_and_option_tags_are_rejected() {
let config = CompactConfig::new(Config::standard());
assert!(matches!(
config.deserialize::<bool>(&[2]).unwrap_err(),
Error::InvalidBool(2)
));
assert!(matches!(
config.deserialize::<Option<u64>>(&[2]).unwrap_err(),
Error::InvalidBool(2)
));
}
#[test]
fn serialized_size_matches_output() {
let config = CompactConfig::new(Config::standard());
let value = vec!["alpha".to_string(), "beta".to_string()];
let bytes = config.serialize(&value).unwrap();
assert_eq!(
config.serialized_size(&value).unwrap() as usize,
bytes.len()
);
}
#[test]
fn slice_serialize_into_slice() {
let config = CompactConfig::new(Config::standard());
let value = vec![1u64, 2, 3];
let bytes = config.serialize(&value).unwrap();
let mut output = [0u8; 16];
let written = config.serialize_into_slice(&mut output, &value).unwrap();
assert_eq!(&output[..written], &bytes[..]);
let mut tiny = [0u8; 2];
let error = config.serialize_into_slice(&mut tiny, &value).unwrap_err();
assert!(matches!(error, Error::BufferTooSmall { .. }));
}
#[test]
fn encode_depth_is_bounded() {
let value = vec![vec![vec![1u64]]];
let error = CompactConfig::new(Config::standard().with_depth_limit(1))
.serialize(&value)
.unwrap_err();
assert!(matches!(error, Error::Custom(_)));
}
#[test]
fn tuple_members_follow_declaration_order() {
let config = CompactConfig::new(Config::standard());
let bytes = config.serialize(&(1u8, 2u16, 3u32)).unwrap();
assert_eq!(bytes, &[1, 2, 3]);
}
#[test]
fn empty_values_encode_minimally() {
let config = CompactConfig::new(Config::standard());
assert_eq!(config.serialize(&()).unwrap(), &[]);
assert_eq!(config.serialize(&Vec::<u64>::new()).unwrap(), &[0]);
assert_eq!(config.serialize(&String::new()).unwrap(), &[0]);
assert_eq!(config.serialize(&[0u64; 0]).unwrap(), &[]);
}
}