#![allow(dead_code)]
use core::iter::FromIterator;
use corez::io::{self, Read, Write};
pub mod version {
pub const V1: u8 = 1;
pub const V2: u8 = 2;
}
pub trait ZainoVersionedSerde: Sized {
const VERSION: u8;
fn encode_latest<W: Write>(&self, w: &mut W) -> io::Result<()>;
fn decode_latest<R: Read>(r: &mut R) -> io::Result<Self>;
#[inline(always)]
#[allow(unused)]
fn encode_v1<W: Write>(&self, _w: &mut W) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"v1 encode unsupported",
))
}
#[inline(always)]
#[allow(unused)]
fn encode_v2<W: Write>(&self, _w: &mut W) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"v2 encode unsupported",
))
}
#[inline(always)]
#[allow(unused)]
fn decode_v1<R: Read>(r: &mut R) -> io::Result<Self> {
Err(io::Error::new(io::ErrorKind::InvalidData, "v1 unsupported"))
}
#[inline(always)]
#[allow(unused)]
fn decode_v2<R: Read>(r: &mut R) -> io::Result<Self> {
Err(io::Error::new(io::ErrorKind::InvalidData, "v2 unsupported"))
}
#[inline]
fn encode_body<W: Write>(&self, w: &mut W, version_tag: u8) -> io::Result<()> {
if version_tag == Self::VERSION {
self.encode_latest(w)
} else {
match version_tag {
version::V1 => self.encode_v1(w),
version::V2 => self.encode_v2(w),
_ => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("unsupported encode version {version_tag}"),
)),
}
}
}
#[inline]
fn decode_body<R: Read>(r: &mut R, version_tag: u8) -> io::Result<Self> {
if version_tag == Self::VERSION {
Self::decode_latest(r)
} else {
match version_tag {
version::V1 => Self::decode_v1(r),
version::V2 => Self::decode_v2(r),
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unsupported Zaino version tag {version_tag}"),
)),
}
}
}
#[inline]
fn serialize<W: Write>(&self, mut w: W) -> io::Result<()> {
self.serialize_with_version(&mut w, Self::VERSION)
}
#[inline]
fn serialize_with_version<W: Write>(&self, mut w: W, version: u8) -> io::Result<()> {
w.write_all(&[version])?;
self.encode_body(&mut w, version)
}
#[inline]
fn deserialize<R: Read>(mut r: R) -> io::Result<Self> {
let mut tag = [0u8; 1];
r.read_exact(&mut tag)?;
Self::decode_body(&mut r, tag[0])
}
#[inline]
fn to_bytes(&self) -> io::Result<Vec<u8>> {
self.to_bytes_with_version(Self::VERSION)
}
#[inline]
fn to_bytes_with_version(&self, version: u8) -> io::Result<Vec<u8>> {
let mut buf = Vec::new();
self.serialize_with_version(&mut buf, version)?;
Ok(buf)
}
#[inline]
fn from_bytes(data: &[u8]) -> io::Result<Self> {
let mut cursor = corez::io::Cursor::new(data);
Self::deserialize(&mut cursor)
}
}
pub trait FixedEncodedLen {
const ENCODED_LEN: usize;
const VERSION_TAG_LEN: usize = 1;
const VERSIONED_LEN: usize = Self::ENCODED_LEN + Self::VERSION_TAG_LEN;
}
pub struct CompactSize;
pub const MAX_COMPACT_SIZE: u32 = 0x0200_0000;
impl CompactSize {
pub fn read<R: Read>(mut reader: R) -> io::Result<u64> {
let mut flag_bytes = [0; 1];
reader.read_exact(&mut flag_bytes)?;
let flag = flag_bytes[0];
let result = if flag < 253 {
Ok(flag as u64)
} else if flag == 253 {
let mut bytes = [0; 2];
reader.read_exact(&mut bytes)?;
match u16::from_le_bytes(bytes) {
n if n < 253 => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"non-canonical CompactSize",
)),
n => Ok(n as u64),
}
} else if flag == 254 {
let mut bytes = [0; 4];
reader.read_exact(&mut bytes)?;
match u32::from_le_bytes(bytes) {
n if n < 0x10000 => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"non-canonical CompactSize",
)),
n => Ok(n as u64),
}
} else {
let mut bytes = [0; 8];
reader.read_exact(&mut bytes)?;
match u64::from_le_bytes(bytes) {
n if n < 0x100000000 => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"non-canonical CompactSize",
)),
n => Ok(n),
}
}?;
match result {
s if s > <u64>::from(MAX_COMPACT_SIZE) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"CompactSize too large",
)),
s => Ok(s),
}
}
pub fn read_t<R: Read, T: TryFrom<u64>>(mut reader: R) -> io::Result<T> {
let n = Self::read(&mut reader)?;
<T>::try_from(n).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"CompactSize value exceeds range of target type.",
)
})
}
pub fn write<W: Write>(mut writer: W, size: usize) -> io::Result<()> {
match size {
s if s < 253 => writer.write_all(&[s as u8]),
s if s <= 0xFFFF => {
writer.write_all(&[253])?;
writer.write_all(&(s as u16).to_le_bytes())
}
s if s <= 0xFFFFFFFF => {
writer.write_all(&[254])?;
writer.write_all(&(s as u32).to_le_bytes())
}
s => {
writer.write_all(&[255])?;
writer.write_all(&(s as u64).to_le_bytes())
}
}
}
pub fn serialized_size(size: usize) -> usize {
match size {
s if s < 253 => 1,
s if s <= 0xFFFF => 3,
s if s <= 0xFFFFFFFF => 5,
_ => 9,
}
}
}
#[inline]
pub fn read_u8<R: Read>(mut r: R) -> io::Result<u8> {
let mut buf = [0u8; 1];
r.read_exact(&mut buf)?;
Ok(buf[0])
}
#[inline]
pub fn write_u8<W: Write>(mut w: W, v: u8) -> io::Result<()> {
w.write_all(&[v])
}
#[inline]
pub fn read_u16_le<R: Read>(mut r: R) -> io::Result<u16> {
let mut buf = [0u8; 2];
r.read_exact(&mut buf)?;
Ok(u16::from_le_bytes(buf))
}
#[inline]
pub fn read_u16_be<R: Read>(mut r: R) -> io::Result<u16> {
let mut buf = [0u8; 2];
r.read_exact(&mut buf)?;
Ok(u16::from_be_bytes(buf))
}
#[inline]
pub fn write_u16_le<W: Write>(mut w: W, v: u16) -> io::Result<()> {
w.write_all(&v.to_le_bytes())
}
#[inline]
pub fn write_u16_be<W: Write>(mut w: W, v: u16) -> io::Result<()> {
w.write_all(&v.to_be_bytes())
}
#[inline]
pub fn read_u32_le<R: Read>(mut r: R) -> io::Result<u32> {
let mut buf = [0u8; 4];
r.read_exact(&mut buf)?;
Ok(u32::from_le_bytes(buf))
}
#[inline]
pub fn read_u32_be<R: Read>(mut r: R) -> io::Result<u32> {
let mut buf = [0u8; 4];
r.read_exact(&mut buf)?;
Ok(u32::from_be_bytes(buf))
}
#[inline]
pub fn write_u32_le<W: Write>(mut w: W, v: u32) -> io::Result<()> {
w.write_all(&v.to_le_bytes())
}
#[inline]
pub fn write_u32_be<W: Write>(mut w: W, v: u32) -> io::Result<()> {
w.write_all(&v.to_be_bytes())
}
#[inline]
pub fn read_u64_le<R: Read>(mut r: R) -> io::Result<u64> {
let mut buf = [0u8; 8];
r.read_exact(&mut buf)?;
Ok(u64::from_le_bytes(buf))
}
#[inline]
pub fn read_u64_be<R: Read>(mut r: R) -> io::Result<u64> {
let mut buf = [0u8; 8];
r.read_exact(&mut buf)?;
Ok(u64::from_be_bytes(buf))
}
#[inline]
pub fn write_u64_le<W: Write>(mut w: W, v: u64) -> io::Result<()> {
w.write_all(&v.to_le_bytes())
}
#[inline]
pub fn write_u64_be<W: Write>(mut w: W, v: u64) -> io::Result<()> {
w.write_all(&v.to_be_bytes())
}
#[inline]
pub fn read_i64_le<R: Read>(mut r: R) -> io::Result<i64> {
let mut buf = [0u8; 8];
r.read_exact(&mut buf)?;
Ok(i64::from_le_bytes(buf))
}
#[inline]
pub fn read_i64_be<R: Read>(mut r: R) -> io::Result<i64> {
let mut buf = [0u8; 8];
r.read_exact(&mut buf)?;
Ok(i64::from_be_bytes(buf))
}
#[inline]
pub fn write_i64_le<W: Write>(mut w: W, v: i64) -> io::Result<()> {
w.write_all(&v.to_le_bytes())
}
#[inline]
pub fn write_i64_be<W: Write>(mut w: W, v: i64) -> io::Result<()> {
w.write_all(&v.to_be_bytes())
}
#[inline]
pub fn read_fixed_le<const N: usize, R: Read>(mut r: R) -> io::Result<[u8; N]> {
let mut buf = [0u8; N];
r.read_exact(&mut buf)?;
Ok(buf)
}
#[inline]
pub fn write_fixed_le<const N: usize, W: Write>(mut w: W, bytes: &[u8; N]) -> io::Result<()> {
w.write_all(bytes)
}
#[inline]
pub fn read_fixed_be<const N: usize, R: Read>(mut r: R) -> io::Result<[u8; N]> {
let mut buf = [0u8; N];
r.read_exact(&mut buf)?;
buf.reverse();
Ok(buf)
}
#[inline]
pub fn write_fixed_be<const N: usize, W: Write>(mut w: W, bytes: &[u8; N]) -> io::Result<()> {
let mut tmp = *bytes;
tmp.reverse();
w.write_all(&tmp)
}
pub fn write_option<W, T, F>(mut w: W, value: &Option<T>, mut f: F) -> io::Result<()>
where
W: Write,
F: FnMut(&mut W, &T) -> io::Result<()>,
{
match value {
None => w.write_all(&[0]),
Some(val) => {
w.write_all(&[1])?;
f(&mut w, val)
}
}
}
pub fn read_option<R, T, F>(mut r: R, mut f: F) -> io::Result<Option<T>>
where
R: Read,
F: FnMut(&mut R) -> io::Result<T>,
{
let mut flag = [0u8; 1];
r.read_exact(&mut flag)?;
match flag[0] {
0 => Ok(None),
1 => f(&mut r).map(Some),
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
"non-canonical Option tag",
)),
}
}
pub fn write_vec<W, T, F>(mut w: W, vec: &[T], mut f: F) -> io::Result<()>
where
W: Write,
F: FnMut(&mut W, &T) -> io::Result<()>,
{
CompactSize::write(&mut w, vec.len())?;
for item in vec {
f(&mut w, item)?
}
Ok(())
}
pub fn read_vec<R, T, F>(mut r: R, mut f: F) -> io::Result<Vec<T>>
where
R: Read,
F: FnMut(&mut R) -> io::Result<T>,
{
let len = CompactSize::read(&mut r)? as usize;
let mut v = Vec::with_capacity(len);
for _ in 0..len {
v.push(f(&mut r)?);
}
Ok(v)
}
pub fn read_vec_into<R, T, C, F>(mut r: R, mut f: F) -> io::Result<C>
where
R: Read,
F: FnMut(&mut R) -> io::Result<T>,
C: FromIterator<T>,
{
let len = CompactSize::read(&mut r)? as usize;
(0..len).map(|_| f(&mut r)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use corez::io::Cursor;
#[test]
fn compactsize_roundtrip_various() {
let values: &[usize] = &[
0usize,
1,
10,
252,
253,
254,
1024,
0xFFFFusize,
0x1_0000usize,
MAX_COMPACT_SIZE as usize,
];
for &v in values {
let mut buf = Vec::new();
CompactSize::write(&mut buf, v).expect("write compactsize");
let mut cur = Cursor::new(&buf);
let r = CompactSize::read(&mut cur).expect("read compactsize");
assert_eq!(r as usize, v, "compactsize roundtrip mismatch for {}", v);
assert_eq!(CompactSize::serialized_size(v), buf.len());
}
}
#[test]
fn compactsize_too_large_errors() {
let too_big = (MAX_COMPACT_SIZE as usize) + 1;
let mut buf = Vec::new();
CompactSize::write(&mut buf, too_big).expect("write oversized");
assert!(
CompactSize::read(Cursor::new(&buf)).is_err(),
"reading compactsize > MAX_COMPAC T_SIZE should error"
);
}
#[test]
fn compactsize_read_t_roundtrip() {
let mut buf = Vec::new();
CompactSize::write(&mut buf, 1000).expect("write 1000");
let mut cur = Cursor::new(&buf);
let v: u32 = CompactSize::read_t(&mut cur).expect("read_t to u32");
assert_eq!(v, 1000u32);
}
#[test]
fn u8_roundtrip() {
let mut buf = Vec::new();
write_u8(&mut buf, 0xAB).expect("write_u8");
let v = read_u8(Cursor::new(&buf)).expect("read_u8");
assert_eq!(v, 0xAB);
}
#[test]
fn u16_le_roundtrip() {
let mut buf = Vec::new();
write_u16_le(&mut buf, 0x1234).expect("write_u16_le");
let v = read_u16_le(Cursor::new(&buf)).expect("read_u16_le");
assert_eq!(v, 0x1234);
}
#[test]
fn u16_be_roundtrip() {
let mut buf = Vec::new();
write_u16_be(&mut buf, 0x1234).expect("write_u16_be");
let v = read_u16_be(Cursor::new(&buf)).expect("read_u16_be");
assert_eq!(v, 0x1234);
}
#[test]
fn u32_le_roundtrip() {
let mut buf = Vec::new();
write_u32_le(&mut buf, 0x1122_3344).expect("write_u32_le");
let v = read_u32_le(Cursor::new(&buf)).expect("read_u32_le");
assert_eq!(v, 0x1122_3344);
}
#[test]
fn u32_be_roundtrip() {
let mut buf = Vec::new();
write_u32_be(&mut buf, 0x1122_3344).expect("write_u32_be");
let v = read_u32_be(Cursor::new(&buf)).expect("read_u32_be");
assert_eq!(v, 0x1122_3344);
}
#[test]
fn u64_le_roundtrip() {
let mut buf = Vec::new();
write_u64_le(&mut buf, 0x0102_0304_0506_0708).expect("write_u64_le");
let v = read_u64_le(Cursor::new(&buf)).expect("read_u64_le");
assert_eq!(v, 0x0102_0304_0506_0708u64);
}
#[test]
fn u64_be_roundtrip() {
let mut buf = Vec::new();
write_u64_be(&mut buf, 0x0102_0304_0506_0708).expect("write_u64_be");
let v = read_u64_be(Cursor::new(&buf)).expect("read_u64_be");
assert_eq!(v, 0x0102_0304_0506_0708u64);
}
#[test]
fn i64_le_roundtrip() {
let mut buf = Vec::new();
let val: i64 = -9_001_234_567_890i64;
write_i64_le(&mut buf, val).expect("write_i64_le");
let r = read_i64_le(Cursor::new(&buf)).expect("read_i64_le");
assert_eq!(r, val);
}
#[test]
fn i64_be_roundtrip() {
let mut buf = Vec::new();
let val: i64 = -9_001_234_567_890i64;
write_i64_be(&mut buf, val).expect("write_i64_be");
let r = read_i64_be(Cursor::new(&buf)).expect("read_i64_be");
assert_eq!(r, val);
}
#[test]
fn fixed_le_roundtrip() {
let arr: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
let mut buf = Vec::new();
write_fixed_le::<8, _>(&mut buf, &arr).expect("write_fixed_le");
let got: [u8; 8] = read_fixed_le::<8, _>(Cursor::new(&buf)).expect("read_fixed_le");
assert_eq!(got, arr);
}
#[test]
fn fixed_be_roundtrip() {
let arr: [u8; 8] = [10, 11, 12, 13, 14, 15, 16, 17];
let mut buf = Vec::new();
write_fixed_be::<8, _>(&mut buf, &arr).expect("write_fixed_be");
let got: [u8; 8] = read_fixed_be::<8, _>(Cursor::new(&buf)).expect("read_fixed_be");
assert_eq!(got, arr);
}
#[test]
fn option_none_roundtrip() {
let mut buf = Vec::new();
write_option(&mut buf, &None::<u32>, |_w, _v| Ok(())).expect("write_option none");
let mut cur = Cursor::new(&buf);
let r: Option<u32> = read_option(&mut cur, |_r| unreachable!()).expect("read_option none");
assert!(r.is_none());
}
#[test]
fn option_some_roundtrip() {
let mut buf = Vec::new();
write_option(&mut buf, &Some(0xDEADBEEFu32), |w, v| write_u32_le(w, *v))
.expect("write_option some");
let mut cur = Cursor::new(&buf);
let r: Option<u32> = read_option(&mut cur, |r| read_u32_le(r)).expect("read_option some");
assert_eq!(r, Some(0xDEADBEEF));
}
#[test]
fn write_vec_read_vec_roundtrip() {
let items = vec![1u16, 2u16, 3u16, 0xABCDu16];
let mut buf = Vec::new();
write_vec(&mut buf, &items, |w, v| write_u16_le(w, *v)).expect("write_vec");
let mut cur = Cursor::new(&buf);
let r: Vec<u16> = read_vec(&mut cur, |r| read_u16_le(r)).expect("read_vec");
assert_eq!(r, items);
}
#[test]
fn read_vec_into_roundtrip() {
let items = vec![10u32, 11u32, 12u32];
let mut buf = Vec::new();
write_vec(&mut buf, &items, |w, v| write_u32_le(w, *v)).expect("write_vec u32");
let mut cur = Cursor::new(&buf);
let out: Vec<u32> = read_vec_into(&mut cur, |r| read_u32_le(r)).expect("read_vec_into");
assert_eq!(out, items);
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Inner {
pub x: u32,
}
impl ZainoVersionedSerde for Inner {
const VERSION: u8 = version::V2;
fn encode_latest<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.encode_v2(w)
}
fn decode_latest<R: Read>(r: &mut R) -> io::Result<Self> {
Self::decode_v2(r)
}
fn encode_v1<W: Write>(&self, w: &mut W) -> io::Result<()> {
write_u32_le(w, self.x)
}
fn encode_v2<W: Write>(&self, w: &mut W) -> io::Result<()> {
write_u32_be(w, self.x)
}
fn decode_v1<R: Read>(r: &mut R) -> io::Result<Self> {
let x = read_u32_le(r)?;
Ok(Inner { x })
}
fn decode_v2<R: Read>(r: &mut R) -> io::Result<Self> {
let x = read_u32_be(r)?;
Ok(Inner { x })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Outer {
pub inner: Inner,
pub y: u8,
}
impl ZainoVersionedSerde for Outer {
const VERSION: u8 = version::V2;
fn encode_latest<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.encode_v2(w)
}
fn decode_latest<R: Read>(r: &mut R) -> io::Result<Self> {
Self::decode_v2(r)
}
fn encode_v1<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.inner.serialize_with_version(&mut *w, version::V1)?;
w.write_all(&[self.y])?;
Ok(())
}
fn encode_v2<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.inner.serialize_with_version(&mut *w, version::V2)?;
w.write_all(&[self.y])?;
Ok(())
}
fn decode_v1<R: Read>(r: &mut R) -> io::Result<Self> {
let inner = Inner::deserialize(&mut *r)?;
let mut buf = [0u8; 1];
r.read_exact(&mut buf)?;
Ok(Outer { inner, y: buf[0] })
}
fn decode_v2<R: Read>(r: &mut R) -> io::Result<Self> {
let inner = Inner::deserialize(&mut *r)?;
let mut buf = [0u8; 1];
r.read_exact(&mut buf)?;
Ok(Outer { inner, y: buf[0] })
}
}
#[test]
fn inner_v1_v2_roundtrip_and_difference() {
let i = Inner { x: 0x1122_3344 };
let b_v1 = i.to_bytes_with_version(version::V1).expect("v1 bytes");
let b_v2 = i.to_bytes_with_version(version::V2).expect("v2 bytes");
assert_ne!(b_v1, b_v2, "v1 and v2 encodings must differ for the test");
let decoded_v1 = Inner::from_bytes(&b_v1).expect("decode v1");
let decoded_v2 = Inner::from_bytes(&b_v2).expect("decode v2");
assert_eq!(decoded_v1, i);
assert_eq!(decoded_v2, i);
assert_eq!(b_v1[0], version::V1);
assert_eq!(b_v2[0], version::V2);
let body_v1 = &b_v1[1..];
let body_v2 = &b_v2[1..];
assert_eq!(body_v1[0], 0x44);
assert_eq!(body_v2[0], 0x11);
}
#[test]
fn outer_nested_v1_v2_roundtrip_and_nested_tag_behavior() {
let o = Outer {
inner: Inner { x: 0xAABBCCDD },
y: 0x7f,
};
let top_v1 = o.to_bytes_with_version(version::V1).expect("outer v1");
let top_v2 = o.to_bytes_with_version(version::V2).expect("outer v2");
assert_eq!(top_v1[0], version::V1);
assert_eq!(top_v2[0], version::V2);
assert!(top_v1.len() >= 2 && top_v2.len() >= 2);
let nested_tag_v1 = top_v1[1];
let nested_tag_v2 = top_v2[1];
assert_eq!(nested_tag_v1, version::V1);
assert_eq!(nested_tag_v2, version::V2);
let decoded_v1 = Outer::from_bytes(&top_v1).expect("decode outer v1");
let decoded_v2 = Outer::from_bytes(&top_v2).expect("decode outer v2");
assert_eq!(decoded_v1, o);
assert_eq!(decoded_v2, o);
}
#[test]
fn serialize_and_deserialize_helpers_consistency() {
let i = Inner { x: 0x0102_0304 };
let latest_bytes = i.to_bytes().expect("latest bytes");
assert_eq!(latest_bytes[0], version::V2);
let v1_bytes = i.to_bytes_with_version(version::V1).expect("v1 bytes");
let v2_bytes = i.to_bytes_with_version(version::V2).expect("v2 bytes");
assert_eq!(v1_bytes[0], version::V1);
assert_eq!(v2_bytes[0], version::V2);
assert_eq!(Inner::from_bytes(&v1_bytes).expect("from v1"), i);
assert_eq!(Inner::from_bytes(&v2_bytes).expect("from v2"), i);
assert_eq!(Inner::from_bytes(&latest_bytes).expect("from latest"), i);
}
#[test]
fn nested_encoding_must_use_serialize_with_version() {
let o = Outer {
inner: Inner { x: 0xDEAD_BEEF },
y: 0x42,
};
let top_v1 = o.to_bytes_with_version(version::V1).expect("outer v1");
assert_eq!(
top_v1[1],
version::V1,
"nested inner tag must be v1 for top-level v1"
);
let decoded = Outer::from_bytes(&top_v1).expect("decode outer v1");
assert_eq!(decoded, o);
}
}