use std::io::{Read, Write};
use crate::Error;
use crate::optimised::tag::{SizeClass, Tag, read_tag, write_tag};
use crate::slice_reader::{BorrowedReader, advance_read};
#[doc(hidden)]
#[inline]
pub fn encode_inline<W: Write>(w: &mut W, variant_id: u8) -> Result<(), Error> {
write_tag(w, Tag::new(variant_id, SizeClass::Inline))
}
#[doc(hidden)]
#[inline]
pub fn encode_fixed<W: Write, F>(w: &mut W, variant_id: u8, body: F) -> Result<(), Error>
where
F: FnOnce(&mut W) -> Result<(), Error>,
{
write_tag(w, Tag::new(variant_id, SizeClass::Fixed))?;
body(w)
}
#[doc(hidden)]
pub fn encode_varlen<W: Write, F>(w: &mut W, variant_id: u8, body: F) -> Result<(), Error>
where
F: FnOnce(&mut Vec<u8>) -> Result<(), Error>,
{
write_tag(w, Tag::new(variant_id, SizeClass::Varlen))?;
let mut scratch: Vec<u8> = Vec::new();
body(&mut scratch)?;
let len: u32 = scratch
.len()
.try_into()
.map_err(|_| Error::Serialize("optimised varlen payload exceeds u32::MAX bytes".into()))?;
w.write_all(&len.to_le_bytes()).map_err(Error::Io)?;
w.write_all(&scratch).map_err(Error::Io)
}
#[doc(hidden)]
#[inline]
pub fn read_optimised_tag<R: Read>(r: &mut R) -> Result<(Tag, SizeClass), Error> {
let tag = read_tag(r)?;
let sc = tag.size_class()?;
Ok((tag, sc))
}
#[doc(hidden)]
#[inline]
pub fn read_varlen_len<R: Read>(r: &mut R) -> Result<u32, Error> {
let mut buf = [0u8; 4];
r.read_exact(&mut buf).map_err(Error::Io)?;
Ok(u32::from_le_bytes(buf))
}
#[doc(hidden)]
#[inline]
pub fn read_varlen_slice<R: BorrowedReader>(r: &mut R) -> Result<&[u8], Error> {
let len = read_varlen_len(r)? as usize;
crate::slice_reader::read_borrowed_bytes(r, len)
}
#[doc(hidden)]
#[inline]
pub fn skip_varlen<R: Read>(r: &mut R) -> Result<(), Error> {
let len = read_varlen_len(r)? as usize;
advance_read(r, len)
}
#[doc(hidden)]
#[inline]
pub fn skip_varlen_borrowed<R: BorrowedReader>(r: &mut R) -> Result<(), Error> {
let len = read_varlen_len(r)? as usize;
r.advance(len)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::slice_reader::SliceReader;
#[test]
fn inline_round_trip() {
let mut buf = Vec::new();
encode_inline(&mut buf, 3).unwrap();
assert_eq!(buf.len(), 1);
let mut r: &[u8] = &buf;
let (tag, sc) = read_optimised_tag(&mut r).unwrap();
assert_eq!(tag.variant_id(), 3);
assert_eq!(sc, SizeClass::Inline);
assert_eq!(r.len(), 0);
}
#[test]
fn fixed_round_trip() {
let mut buf = Vec::new();
encode_fixed(&mut buf, 7, |w| {
w.write_all(&42i64.to_le_bytes()).map_err(Error::Io)?;
Ok(())
})
.unwrap();
assert_eq!(buf.len(), 1 + 8);
let mut r: &[u8] = &buf;
let (tag, sc) = read_optimised_tag(&mut r).unwrap();
assert_eq!(tag.variant_id(), 7);
assert_eq!(sc, SizeClass::Fixed);
let mut payload = [0u8; 8];
r.read_exact(&mut payload).unwrap();
assert_eq!(i64::from_le_bytes(payload), 42);
}
#[test]
fn varlen_round_trip() {
let mut buf = Vec::new();
encode_varlen(&mut buf, 12, |scratch| {
scratch.extend_from_slice(b"hello world");
Ok(())
})
.unwrap();
assert_eq!(buf.len(), 1 + 4 + 11);
let mut r = SliceReader::new(&buf);
let (tag, sc) = read_optimised_tag(&mut r).unwrap();
assert_eq!(tag.variant_id(), 12);
assert_eq!(sc, SizeClass::Varlen);
let payload = read_varlen_slice(&mut r).unwrap();
assert_eq!(payload, b"hello world");
}
#[test]
fn skip_varlen_advances_full_length() {
let mut buf = Vec::new();
encode_varlen(&mut buf, 1, |s| {
s.extend_from_slice(&[0xAA; 100]);
Ok(())
})
.unwrap();
let mut r: &[u8] = &buf;
let (_, sc) = read_optimised_tag(&mut r).unwrap();
assert_eq!(sc, SizeClass::Varlen);
skip_varlen(&mut r).unwrap();
assert_eq!(r.len(), 0);
}
#[test]
fn skip_varlen_borrowed_matches_skip_varlen() {
let mut buf = Vec::new();
encode_varlen(&mut buf, 1, |s| {
s.extend_from_slice(&[0xAA; 100]);
Ok(())
})
.unwrap();
let mut r = SliceReader::new(&buf);
let (_, _) = read_optimised_tag(&mut r).unwrap();
skip_varlen_borrowed(&mut r).unwrap();
assert!(r.remaining().is_empty());
}
#[test]
fn read_optimised_tag_errors_on_reserved_size_class() {
let bad = 0b0110_0000;
let mut r: &[u8] = &[bad];
let err = read_optimised_tag(&mut r).unwrap_err();
assert!(matches!(err, Error::InvalidOptimisedTag(b) if b == bad));
}
#[test]
fn read_varlen_slice_overrun_errors() {
let mut buf = Vec::new();
buf.push(Tag::new(0, SizeClass::Varlen).0);
buf.extend_from_slice(&100u32.to_le_bytes());
buf.extend_from_slice(&[0u8; 4]);
let mut r = SliceReader::new(&buf);
let _ = read_optimised_tag(&mut r).unwrap();
assert!(read_varlen_slice(&mut r).is_err());
}
}