pub struct ConstWriter<const N: usize> {
buf: [u8; N],
pos: usize,
}
const V_MULTIBYTE: u8 = 63;
const S_8BYTE: u8 = 3 << 6;
const S_4BYTE: u8 = 2 << 6;
const S_2BYTE: u8 = 1 << 6;
const S_1BYTE: u8 = 0 << 6;
const S_HEADER: u8 = 0 << 6;
const IH_MAX: i8 = 31;
const IH_MIN: i8 = -31;
const UH_MAX: u8 = 62;
impl<const N: usize> Default for ConstWriter<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> ConstWriter<N> {
pub const fn new() -> Self {
Self {
buf: [0u8; N],
pos: 0,
}
}
pub const fn pos(&self) -> usize {
self.pos
}
pub const fn into_bytes(self) -> [u8; N] {
assert!(
self.pos == N,
"ConstWriter: bytes written does not equal buffer size N"
);
self.buf
}
const fn put_byte(&mut self, b: u8) {
self.buf[self.pos] = b;
self.pos += 1;
}
const fn put_bytes(&mut self, bytes: &[u8]) {
let mut i = 0;
while i < bytes.len() {
self.buf[self.pos + i] = bytes[i];
i += 1;
}
self.pos += bytes.len();
}
#[doc(hidden)]
pub const fn write_opaque_fixed(&mut self, bytes: &[u8]) {
assert!(
bytes.len() <= u32::MAX as usize,
"opaque length exceeds u32::MAX"
);
self.put_bytes(bytes);
}
pub const fn write_opaque_variable(&mut self, bytes: &[u8]) {
assert!(
bytes.len() <= u32::MAX as usize,
"opaque length exceeds u32::MAX"
);
self.write_u32(bytes.len() as u32);
self.put_bytes(bytes);
}
pub const fn write_bool(&mut self, v: bool) {
self.write_i32(if v { 1 } else { 0 });
}
pub const fn write_f32(&mut self, n: f32) {
let b = n.to_le_bytes();
self.put_bytes(&b);
}
pub const fn write_f64(&mut self, n: f64) {
let b = n.to_le_bytes();
self.put_bytes(&b);
}
pub const fn write_i64(&mut self, n: i64) {
if n > i32::MAX as i64 || n < (i32::MIN as i64) {
let h = V_MULTIBYTE | S_8BYTE;
let b = n.to_le_bytes();
let combined = [h, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]];
self.put_bytes(&combined);
} else {
self.write_i32(n as i32);
}
}
pub const fn write_i32(&mut self, n: i32) {
if n > i16::MAX as i32 || n < (i16::MIN as i32) {
let h = V_MULTIBYTE | S_4BYTE;
let b = n.to_le_bytes();
let combined = [h, b[0], b[1], b[2], b[3]];
self.put_bytes(&combined);
} else {
self.write_i16(n as i16);
}
}
pub const fn write_i16(&mut self, n: i16) {
if n > i8::MAX as i16 || n < (i8::MIN as i16) {
let h = V_MULTIBYTE | S_2BYTE;
let b = n.to_le_bytes();
let combined = [h, b[0], b[1]];
self.put_bytes(&combined);
} else {
self.const_write_i8(n as i8);
}
}
const fn const_write_i8(&mut self, n: i8) {
if n < IH_MIN || n > IH_MAX {
let h = V_MULTIBYTE | S_1BYTE;
let combined = [h, n as u8];
self.put_bytes(&combined);
} else {
let nu: u8 = n.unsigned_abs();
let v = if n >= 0 {
nu
} else {
(!nu & 0b01_1111) | 0b0010_0000
};
let h = v | S_HEADER;
self.put_byte(h);
}
}
#[cfg(feature = "int128")]
#[cfg_attr(docsrs, doc(cfg(feature = "int128")))]
pub const fn write_i128(&mut self, n: i128) {
self.write_i64((n >> 64) as i64);
self.write_u64((n & 0xffffffffffffffff) as u64);
}
#[cfg(feature = "int128")]
#[cfg_attr(docsrs, doc(cfg(feature = "int128")))]
pub const fn write_u128(&mut self, n: u128) {
self.write_u64((n >> 64) as u64);
self.write_u64((n & 0xffffffffffffffff) as u64);
}
pub const fn write_u64(&mut self, n: u64) {
if n > u32::MAX as u64 {
let h = V_MULTIBYTE | S_8BYTE;
let b = n.to_le_bytes();
let combined = [h, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]];
self.put_bytes(&combined);
} else {
self.write_u32(n as u32);
}
}
pub const fn write_u32(&mut self, n: u32) {
if n > u16::MAX as u32 {
let h = V_MULTIBYTE | S_4BYTE;
let b = n.to_le_bytes();
let combined = [h, b[0], b[1], b[2], b[3]];
self.put_bytes(&combined);
} else {
self.write_u16(n as u16);
}
}
pub const fn write_u16(&mut self, n: u16) {
if n > u8::MAX as u16 {
let h = V_MULTIBYTE | S_2BYTE;
let b = n.to_le_bytes();
let combined = [h, b[0], b[1]];
self.put_bytes(&combined);
} else {
self.const_write_u8(n as u8);
}
}
const fn const_write_u8(&mut self, n: u8) {
if n > UH_MAX {
let h = V_MULTIBYTE | S_1BYTE;
let combined = [h, n];
self.put_bytes(&combined);
} else {
self.put_byte(n);
}
}
}
pub const fn size_bool(v: bool) -> usize {
size_i32(if v { 1 } else { 0 })
}
pub const fn size_f32(_n: f32) -> usize {
4
}
pub const fn size_f64(_n: f64) -> usize {
8
}
pub const fn size_i64(n: i64) -> usize {
if n > i32::MAX as i64 || n < (i32::MIN as i64) {
9 } else {
size_i32(n as i32)
}
}
pub const fn size_i32(n: i32) -> usize {
if n > i16::MAX as i32 || n < (i16::MIN as i32) {
5 } else {
size_i16(n as i16)
}
}
pub const fn size_i16(n: i16) -> usize {
if n > i8::MAX as i16 || n < (i8::MIN as i16) {
3 } else {
size_i8(n as i8)
}
}
const fn size_i8(n: i8) -> usize {
if n < IH_MIN || n > IH_MAX {
2 } else {
1 }
}
#[cfg(feature = "int128")]
#[cfg_attr(docsrs, doc(cfg(feature = "int128")))]
pub const fn size_i128(n: i128) -> usize {
size_i64((n >> 64) as i64) + size_u64((n & 0xffffffffffffffff) as u64)
}
#[cfg(feature = "int128")]
#[cfg_attr(docsrs, doc(cfg(feature = "int128")))]
pub const fn size_u128(n: u128) -> usize {
size_u64((n >> 64) as u64) + size_u64((n & 0xffffffffffffffff) as u64)
}
pub const fn size_u64(n: u64) -> usize {
if n > u32::MAX as u64 {
9
} else {
size_u32(n as u32)
}
}
pub const fn size_u32(n: u32) -> usize {
if n > u16::MAX as u32 {
5
} else {
size_u16(n as u16)
}
}
pub const fn size_u16(n: u16) -> usize {
if n > u8::MAX as u16 {
3
} else {
size_u8(n as u8)
}
}
const fn size_u8(n: u8) -> usize {
if n > UH_MAX { 2 } else { 1 }
}
pub const fn size_opaque_fixed(len: usize) -> usize {
assert!(len <= u32::MAX as usize, "opaque length exceeds u32::MAX");
len
}
pub const fn size_opaque_variable(len: usize) -> usize {
assert!(len <= u32::MAX as usize, "opaque length exceeds u32::MAX");
size_u32(len as u32) + len
}
pub const fn max_size_bool() -> usize {
1
}
pub const fn max_size_f32() -> usize {
4
}
pub const fn max_size_f64() -> usize {
8
}
pub const fn max_size_i16() -> usize {
3
}
pub const fn max_size_u16() -> usize {
3
}
pub const fn max_size_i32() -> usize {
5
}
pub const fn max_size_u32() -> usize {
5
}
pub const fn max_size_i64() -> usize {
9
}
pub const fn max_size_u64() -> usize {
9
}
#[cfg(feature = "int128")]
#[cfg_attr(docsrs, doc(cfg(feature = "int128")))]
pub const fn max_size_i128() -> usize {
18
}
#[cfg(feature = "int128")]
#[cfg_attr(docsrs, doc(cfg(feature = "int128")))]
pub const fn max_size_u128() -> usize {
18
}
pub const fn max_size_opaque_fixed(len: usize) -> Option<usize> {
if len > u32::MAX as usize {
None
} else {
Some(len)
}
}
pub const fn max_size_opaque_variable(max_len: u32) -> Option<usize> {
size_u32(max_len).checked_add(max_len as usize)
}
pub const fn max_size_add(a: Option<usize>, b: Option<usize>) -> Option<usize> {
match (a, b) {
(Some(a), Some(b)) => a.checked_add(b),
_ => None,
}
}
pub const fn max_size_max(a: Option<usize>, b: Option<usize>) -> Option<usize> {
match (a, b) {
(Some(a), Some(b)) => Some(if a > b { a } else { b }),
_ => None,
}
}
pub const fn max_size_array(len: usize, elem: Option<usize>) -> Option<usize> {
if len == 0 {
return Some(0);
}
if len > u32::MAX as usize {
return None;
}
match elem {
Some(elem) => elem.checked_mul(len),
None => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
const SMALL_INTS: [u8; 4] = {
let mut w = ConstWriter::<4>::new();
w.write_i32(0); w.write_i32(31); w.write_i32(-1); w.write_i32(1); w.into_bytes()
};
#[test]
fn const_encode_small_ints() {
assert_eq!(SMALL_INTS, [0x00, 0x1f, 0x3e, 0x01]);
}
const TWO_BYTE: [u8; 3] = {
let mut w = ConstWriter::<3>::new();
w.write_i32(-32); w.write_i32(1); w.into_bytes()
};
#[test]
fn const_encode_two_byte() {
assert_eq!(TWO_BYTE, [0x3f, 0xe0, 0x01]);
}
const FOUR_BYTE: [u8; 5] = {
let mut w = ConstWriter::<5>::new();
w.write_i32(i32::MAX);
w.into_bytes()
};
#[test]
fn const_encode_four_byte() {
assert_eq!(FOUR_BYTE, [0xbf, 0xff, 0xff, 0xff, 0x7f]);
}
const U32_DB: [u8; 5] = {
let mut w = ConstWriter::<5>::new();
w.write_u32(4022250974); w.into_bytes()
};
#[test]
fn const_encode_u32_deadbeef() {
assert_eq!(U32_DB, [0xbf, 0xde, 0xad, 0xbe, 0xef]);
}
const I64_MAX: [u8; 9] = {
let mut w = ConstWriter::<9>::new();
w.write_i64(i64::MAX);
w.into_bytes()
};
#[test]
fn const_encode_i64_max() {
assert_eq!(
I64_MAX,
[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]
);
}
const OPAQUE: [u8; 3] = {
let mut w = ConstWriter::<3>::new();
w.write_opaque_variable(b"hi");
w.into_bytes()
};
#[test]
fn const_encode_opaque_variable() {
assert_eq!(OPAQUE, [0x02, 0x68, 0x69]);
}
const BOOL_TRUE: [u8; 1] = {
let mut w = ConstWriter::<1>::new();
w.write_bool(true);
w.into_bytes()
};
#[test]
fn const_encode_bool() {
assert_eq!(BOOL_TRUE, [0x01]);
}
#[test]
fn max_sizes_dominate_worst_case_values() {
assert_eq!(max_size_i16(), size_i16(i16::MAX));
assert_eq!(max_size_u16(), size_u16(u16::MAX));
assert_eq!(max_size_i32(), size_i32(i32::MAX));
assert_eq!(max_size_u32(), size_u32(u32::MAX));
assert_eq!(max_size_i64(), size_i64(i64::MAX));
assert_eq!(max_size_u64(), size_u64(u64::MAX));
assert_eq!(max_size_bool(), size_bool(true));
assert_eq!(max_size_f32(), size_f32(0.0));
assert_eq!(max_size_f64(), size_f64(0.0));
}
#[cfg(feature = "int128")]
#[test]
fn max_sizes_dominate_worst_case_int128() {
assert_eq!(max_size_i128(), size_i128(i128::MAX));
assert_eq!(max_size_u128(), size_u128(u128::MAX));
}
#[test]
fn max_size_opaque() {
assert_eq!(max_size_opaque_fixed(0), Some(0));
assert_eq!(max_size_opaque_fixed(7), Some(7));
assert_eq!(max_size_opaque_variable(2), Some(size_opaque_variable(2)));
#[cfg(target_pointer_width = "64")]
{
assert_eq!(
max_size_opaque_variable(u32::MAX),
Some(5 + u32::MAX as usize)
);
}
}
#[test]
fn max_size_folds() {
assert_eq!(max_size_add(Some(3), Some(4)), Some(7));
assert_eq!(max_size_add(Some(3), None), None);
assert_eq!(max_size_add(None, Some(4)), None);
assert_eq!(max_size_add(Some(usize::MAX), Some(1)), None);
assert_eq!(max_size_max(Some(3), Some(4)), Some(4));
assert_eq!(max_size_max(Some(3), None), None);
assert_eq!(max_size_array(4, Some(9)), Some(36));
assert_eq!(max_size_array(0, Some(9)), Some(0));
assert_eq!(max_size_array(0, None), Some(0));
assert_eq!(max_size_array(4, None), None);
#[cfg(target_pointer_width = "64")]
assert_eq!(max_size_array(usize::MAX, Some(1)), None);
assert_eq!(max_size_array(2, Some(usize::MAX)), None);
}
}