use unsigned_varint::encode;
pub const MAX_VARINT_SIZE: usize = 19;
pub trait EncodeIntoArray {
const MAX_ENCODED_SIZE: usize;
fn encode_into_array(&self) -> ([u8; MAX_VARINT_SIZE], usize);
}
macro_rules! impl_encode_into_array {
($($type:ty, $max_size:expr => $buffer_fn:ident, $encode_fn:ident);+ $(;)?) => {
$(
#[doc = concat!("Encode a ", stringify!($type), " into a stack-allocated array")]
impl EncodeIntoArray for $type {
const MAX_ENCODED_SIZE: usize = $max_size;
#[inline]
fn encode_into_array(&self) -> ([u8; MAX_VARINT_SIZE], usize) {
let mut buf = encode::$buffer_fn();
encode::$encode_fn(*self, &mut buf);
let len = buf
.iter()
.position(|&b| unsigned_varint::decode::is_last(b))
.map_or(buf.len(), |pos| pos + 1);
let mut result = [0u8; MAX_VARINT_SIZE];
result[..len].copy_from_slice(&buf[..len]);
(result, len)
}
}
)+
};
}
impl EncodeIntoArray for bool {
const MAX_ENCODED_SIZE: usize = 1;
#[inline]
fn encode_into_array(&self) -> ([u8; MAX_VARINT_SIZE], usize) {
let mut result = [0u8; MAX_VARINT_SIZE];
result[0] = u8::from(*self);
(result, 1)
}
}
impl_encode_into_array! {
u8, 2 => u8_buffer, u8;
u16, 3 => u16_buffer, u16;
u32, 5 => u32_buffer, u32;
u64, 10 => u64_buffer, u64;
u128, 19 => u128_buffer, u128;
}
impl EncodeIntoArray for usize {
const MAX_ENCODED_SIZE: Self = 10;
#[inline]
fn encode_into_array(&self) -> ([u8; MAX_VARINT_SIZE], usize) {
let mut buf = encode::usize_buffer();
encode::usize(*self, &mut buf);
let len = buf
.iter()
.position(|&b| unsigned_varint::decode::is_last(b))
.map_or(buf.len(), |pos| pos + 1);
let mut result = [0u8; MAX_VARINT_SIZE];
result[..len].copy_from_slice(&buf[..len]);
(result, len)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_array_single_byte() {
let (array, len) = 42u8.encode_into_array();
assert_eq!(len, 1);
assert_eq!(&array[..len], &[42]);
}
#[test]
fn test_array_multi_byte() {
let (array, len) = 128u16.encode_into_array();
assert_eq!(len, 2);
assert_eq!(&array[..len], &[0x80, 0x01]);
}
#[test]
fn test_array_bool() {
let (array_true, len_true) = true.encode_into_array();
assert_eq!(len_true, 1);
assert_eq!(&array_true[..len_true], &[1]);
let (array_false, len_false) = false.encode_into_array();
assert_eq!(len_false, 1);
assert_eq!(&array_false[..len_false], &[0]);
}
#[test]
fn test_array_zero_values() {
let (array, len) = 0u8.encode_into_array();
assert_eq!(len, 1);
assert_eq!(&array[..len], &[0]);
let (array, len) = 0u32.encode_into_array();
assert_eq!(len, 1);
assert_eq!(&array[..len], &[0]);
}
#[test]
fn test_array_max_values() {
let (_array, len) = u8::MAX.encode_into_array();
assert!(len <= <u8 as EncodeIntoArray>::MAX_ENCODED_SIZE);
let (_array, len) = u16::MAX.encode_into_array();
assert!(len <= <u16 as EncodeIntoArray>::MAX_ENCODED_SIZE);
let (_array, len) = u32::MAX.encode_into_array();
assert!(len <= <u32 as EncodeIntoArray>::MAX_ENCODED_SIZE);
}
#[test]
fn test_array_max_sizes() {
assert_eq!(<u8 as EncodeIntoArray>::MAX_ENCODED_SIZE, 2);
assert_eq!(<u16 as EncodeIntoArray>::MAX_ENCODED_SIZE, 3);
assert_eq!(<u32 as EncodeIntoArray>::MAX_ENCODED_SIZE, 5);
assert_eq!(<u64 as EncodeIntoArray>::MAX_ENCODED_SIZE, 10);
assert_eq!(<u128 as EncodeIntoArray>::MAX_ENCODED_SIZE, 19);
assert_eq!(<usize as EncodeIntoArray>::MAX_ENCODED_SIZE, 10);
assert_eq!(<bool as EncodeIntoArray>::MAX_ENCODED_SIZE, 1);
}
#[test]
fn test_array_sequential_encoding() {
let (array1, len1) = 42u8.encode_into_array();
let (_array2, len2) = 1000u16.encode_into_array();
let (_array3, len3) = 100_000_u32.encode_into_array();
assert_eq!(&array1[..len1], &[42]);
assert_eq!(len2, 2);
assert!(len3 > 1);
}
#[test]
fn test_array_no_heap_allocation() {
let (_array, len) = u64::MAX.encode_into_array();
assert!(len <= <u64 as EncodeIntoArray>::MAX_ENCODED_SIZE);
assert_eq!(len, 10); }
#[test]
fn test_array_deterministic() {
let (array1, len1) = 12345u32.encode_into_array();
let (array2, len2) = 12345u32.encode_into_array();
assert_eq!(len1, len2);
assert_eq!(&array1[..len1], &array2[..len2]);
}
#[test]
fn test_array_unused_space_is_zeroed() {
let (array, len) = 42u8.encode_into_array();
assert_eq!(len, 1);
for &byte in &array[len..MAX_VARINT_SIZE] {
assert_eq!(byte, 0);
}
}
}