use internals::array_vec::ArrayVec;
use crate::decode::Decoder;
use crate::encode::{Encoder, ExactSizeEncoder};
use crate::error::{
CompactSizeDecoderError, CompactSizeDecoderErrorInner, LengthPrefixExceedsMaxError,
};
const MAX_COMPACT_SIZE: usize = 0x0200_0000;
const SIZE: usize = 9;
const PREFIX_U16: u8 = 0xFD;
const PREFIX_U32: u8 = 0xFE;
const PREFIX_U64: u8 = 0xFF;
#[derive(Debug, Clone)]
pub struct CompactSizeEncoder {
buf: Option<ArrayVec<u8, SIZE>>,
}
impl CompactSizeEncoder {
pub fn new(value: usize) -> Self {
Self { buf: Some(Self::encode(u64::try_from(value).unwrap_or(u64::MAX))) }
}
pub fn new_u64(value: u64) -> Self { Self { buf: Some(Self::encode(value)) } }
#[inline]
pub const fn encoded_size(value: usize) -> usize {
match value {
0..=0xFC => 1,
0xFD..=0xFFFF => 3,
0x10000..=0xFFFF_FFFF => 5,
_ => 9,
}
}
#[inline]
fn encode(value: u64) -> ArrayVec<u8, SIZE> {
let mut res = ArrayVec::<u8, SIZE>::new();
match value {
0..=0xFC => {
res.push(value as u8); }
0xFD..=0xFFFF => {
let v = value as u16; res.push(PREFIX_U16);
res.extend_from_slice(&v.to_le_bytes());
}
0x10000..=0xFFFF_FFFF => {
let v = value as u32; res.push(PREFIX_U32);
res.extend_from_slice(&v.to_le_bytes());
}
_ => {
res.push(PREFIX_U64);
res.extend_from_slice(&value.to_le_bytes());
}
}
res
}
}
impl Encoder for CompactSizeEncoder {
#[inline]
fn current_chunk(&self) -> &[u8] { self.buf.as_ref().map(|b| &b[..]).unwrap_or_default() }
#[inline]
fn advance(&mut self) -> bool {
self.buf = None;
false
}
}
impl ExactSizeEncoder for CompactSizeEncoder {
#[inline]
fn len(&self) -> usize { self.buf.map_or(0, |buf| buf.len()) }
}
#[derive(Debug, Clone)]
pub struct CompactSizeDecoder {
buf: ArrayVec<u8, 9>,
limit: usize,
}
impl CompactSizeDecoder {
pub const fn new() -> Self { Self { buf: ArrayVec::new(), limit: MAX_COMPACT_SIZE } }
pub const fn new_with_limit(limit: usize) -> Self { Self { buf: ArrayVec::new(), limit } }
}
impl Default for CompactSizeDecoder {
fn default() -> Self { Self::new() }
}
impl Decoder for CompactSizeDecoder {
type Output = usize;
type Error = CompactSizeDecoderError;
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
Ok(compact_size_push_bytes(&mut self.buf, bytes))
}
fn end(self) -> Result<Self::Output, Self::Error> {
use CompactSizeDecoderErrorInner as E;
let dec_value = compact_size_decode_u64(&self.buf)?;
let make_err = || {
CompactSizeDecoderError(E::ValueExceedsLimit(LengthPrefixExceedsMaxError {
value: dec_value,
limit: self.limit,
}))
};
usize::try_from(dec_value).map_err(|_| make_err()).and_then(|nsize| {
if nsize > self.limit {
Err(make_err())
} else {
Ok(nsize)
}
})
}
fn read_limit(&self) -> usize { compact_size_read_limit(&self.buf) }
}
#[derive(Debug, Clone)]
pub struct CompactSizeU64Decoder {
buf: ArrayVec<u8, 9>,
}
impl CompactSizeU64Decoder {
pub const fn new() -> Self { Self { buf: ArrayVec::new() } }
}
impl Default for CompactSizeU64Decoder {
fn default() -> Self { Self::new() }
}
impl Decoder for CompactSizeU64Decoder {
type Output = u64;
type Error = CompactSizeDecoderError;
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
Ok(compact_size_push_bytes(&mut self.buf, bytes))
}
fn end(self) -> Result<Self::Output, Self::Error> { compact_size_decode_u64(&self.buf) }
fn read_limit(&self) -> usize { compact_size_read_limit(&self.buf) }
}
fn compact_size_push_bytes(buf: &mut ArrayVec<u8, 9>, bytes: &mut &[u8]) -> bool {
if bytes.is_empty() {
return true;
}
if buf.is_empty() {
buf.push(bytes[0]);
*bytes = &bytes[1..];
}
let len = match buf[0] {
PREFIX_U64 => 9,
PREFIX_U32 => 5,
PREFIX_U16 => 3,
_ => 1,
};
let to_copy = bytes.len().min(len - buf.len());
buf.extend_from_slice(&bytes[..to_copy]);
*bytes = &bytes[to_copy..];
buf.len() != len
}
fn compact_size_read_limit(buf: &ArrayVec<u8, 9>) -> usize {
match buf.len() {
0 => 1,
already_read => match buf[0] {
PREFIX_U64 => 9_usize.saturating_sub(already_read),
PREFIX_U32 => 5_usize.saturating_sub(already_read),
PREFIX_U16 => 3_usize.saturating_sub(already_read),
_ => 0,
},
}
}
fn compact_size_decode_u64(buf: &ArrayVec<u8, 9>) -> Result<u64, CompactSizeDecoderError> {
use CompactSizeDecoderErrorInner as E;
fn arr<const N: usize>(slice: &[u8]) -> Result<[u8; N], CompactSizeDecoderError> {
slice.try_into().map_err(|_| {
CompactSizeDecoderError(E::UnexpectedEof { required: N, received: slice.len() })
})
}
let (first, payload) = buf
.split_first()
.ok_or(CompactSizeDecoderError(E::UnexpectedEof { required: 1, received: 0 }))?;
match *first {
PREFIX_U64 => {
let x = u64::from_le_bytes(arr(payload)?);
if x < 0x100_000_000 {
Err(CompactSizeDecoderError(E::NonMinimal { value: x }))
} else {
Ok(x)
}
}
PREFIX_U32 => {
let x = u32::from_le_bytes(arr(payload)?);
if x < 0x10000 {
Err(CompactSizeDecoderError(E::NonMinimal { value: x.into() }))
} else {
Ok(x.into())
}
}
PREFIX_U16 => {
let x = u16::from_le_bytes(arr(payload)?);
if x < 0xFD {
Err(CompactSizeDecoderError(E::NonMinimal { value: x.into() }))
} else {
Ok(x.into())
}
}
n => Ok(n.into()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encoded_value_1_byte() {
for v in [0x00u64, 0x01, 0x02, 0xFA, 0xFB, 0xFC] {
assert_eq!(CompactSizeEncoder::encoded_size(v as usize), 1);
let want = [v as u8];
let got = CompactSizeEncoder::encode(v);
assert_eq!(got.as_slice().len(), 1); assert_eq!(got.as_slice(), want);
}
}
macro_rules! check_encode {
($($test_name:ident, $size:expr, $value:expr, $want:expr);* $(;)?) => {
$(
#[test]
fn $test_name() {
let value = $value as u64; assert_eq!(CompactSizeEncoder::encoded_size(value as usize), $size);
let got = CompactSizeEncoder::encode(value);
assert_eq!(got.as_slice().len(), $size); assert_eq!(got.as_slice(), &$want);
}
)*
}
}
check_encode! {
encoded_value_3_byte_lower_bound, 3, 0xFD, [0xFD, 0xFD, 0x00]; encoded_value_3_byte_endianness, 3, 0xABCD, [0xFD, 0xCD, 0xAB];
encoded_value_3_byte_upper_bound, 3, 0xFFFF, [0xFD, 0xFF, 0xFF];
encoded_value_5_byte_lower_bound, 5, 0x0001_0000, [0xFE, 0x00, 0x00, 0x01, 0x00];
encoded_value_5_byte_endianness, 5, 0x0123_4567, [0xFE, 0x67, 0x45, 0x23, 0x01];
encoded_value_5_byte_upper_bound, 5, 0xFFFF_FFFF, [0xFE, 0xFF, 0xFF, 0xFF, 0xFF];
}
#[cfg(target_pointer_width = "64")]
check_encode! {
encoded_value_9_byte_lower_bound, 9, 0x0000_0001_0000_0000u64, [0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
encoded_value_9_byte_endianness, 9, 0x0123_4567_89AB_CDEFu64, [0xFF, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01];
encoded_value_9_byte_upper_bound, 9, u64::MAX, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
}
#[test]
fn compact_size_new_values_too_large() {
use CompactSizeDecoderErrorInner as E;
const EXCESS_COMPACT_SIZE: u64 = (MAX_COMPACT_SIZE + 1) as u64;
let mut decoder = CompactSizeDecoder::new();
decoder.push_bytes(&mut [0xFE, 0x00, 0x00, 0x00, 0x02].as_slice()).unwrap();
let got = decoder.end().unwrap();
assert_eq!(got, MAX_COMPACT_SIZE);
let mut decoder = CompactSizeDecoder::new();
decoder.push_bytes(&mut [0xFE, 0x01, 0x00, 0x00, 0x02].as_slice()).unwrap();
let got = decoder.end().unwrap_err();
assert!(matches!(
got,
CompactSizeDecoderError(E::ValueExceedsLimit(LengthPrefixExceedsMaxError {
limit: MAX_COMPACT_SIZE,
value: EXCESS_COMPACT_SIZE,
})),
));
}
#[test]
fn compact_size_new_with_limit_values_too_large() {
use CompactSizeDecoderErrorInner as E;
let mut decoder = CompactSizeDecoder::new_with_limit(240);
decoder.push_bytes(&mut [0xf0].as_slice()).unwrap();
let got = decoder.end().unwrap();
assert_eq!(got, 240);
let mut decoder = CompactSizeDecoder::new_with_limit(240);
decoder.push_bytes(&mut [0xf1].as_slice()).unwrap();
let got = decoder.end().unwrap_err();
assert!(matches!(
got,
CompactSizeDecoderError(E::ValueExceedsLimit(LengthPrefixExceedsMaxError {
limit: 240,
value: 241,
})),
));
}
}