mod codec;
#[cfg(test)]
mod tests;
use crate::{path::ValueShape, property::Kind, Error};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Numbering {
#[default]
Current,
Legacy,
}
impl Numbering {
#[must_use]
pub fn from_legacy(legacy: bool) -> Self {
match legacy {
true => Self::Legacy,
false => Self::Current,
}
}
#[must_use]
pub fn is_legacy(self) -> bool {
matches!(self, Self::Legacy)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Cursor<'a> {
buf: &'a [u8],
pos: usize,
numbering: Numbering,
}
impl<'a> Cursor<'a> {
#[must_use]
pub fn new(buf: &'a [u8], numbering: Numbering) -> Self {
Self {
buf,
pos: 0,
numbering,
}
}
#[must_use]
pub fn numbering(&self) -> Numbering {
self.numbering
}
#[must_use]
pub fn position(&self) -> usize {
self.pos
}
#[must_use]
pub fn remaining(&self) -> usize {
self.buf.len() - self.pos
}
#[must_use]
pub fn rest(&self) -> &'a [u8] {
&self.buf[self.pos..]
}
pub fn take(&mut self, n: usize) -> Result<&'a [u8], Error> {
let end = self.pos.checked_add(n).ok_or_else(eof)?;
let bytes = self.buf.get(self.pos..end).ok_or_else(eof)?;
self.pos = end;
Ok(bytes)
}
pub fn skip(&mut self, n: usize) -> Result<(), Error> {
self.take(n).map(|_| ())
}
pub fn skip_value(&mut self, kind: Kind) -> Result<(), Error> {
use Kind as K;
if let Some(width) = kind.fixed_width() {
return self.skip(width);
}
match kind {
K::String => {
let len = self.u16()? as usize;
self.skip(len)
}
K::Container | K::UnorderedContainer => {
self.skip(1)?; let size = self.u32()? as usize;
self.skip(size)
}
K::Map => {
self.skip(2)?; let size = self.u32()? as usize;
self.skip(size)
}
K::Struct | K::Embedded => {
if self.u32()? == 0 {
return Ok(());
}
let size = self.u32()? as usize;
self.skip(size)
}
K::Optional => {
let item_kind = self.kind()?;
match self.bool()? {
true => self.skip_value(item_kind),
false => Ok(()),
}
}
_ => unreachable!("every kind without a fixed width is matched above"),
}
}
pub fn take_value(&mut self, kind: Kind) -> Result<&'a [u8], Error> {
let start = self.pos;
self.skip_value(kind)?;
Ok(&self.buf[start..self.pos])
}
pub fn value_shape(&self, kind: Kind) -> Result<ValueShape, Error> {
use Kind as K;
let mut ahead = *self;
let (item_kind, key_kind, class) = match kind {
K::Container | K::UnorderedContainer | K::Optional => (Some(ahead.kind()?), None, None),
K::Map => {
let key = ahead.kind()?;
let value = ahead.kind()?;
(Some(value), Some(key), None)
}
K::Embedded => (None, None, Some(ahead.bin_hash()?)),
_ => (None, None, None),
};
Ok(ValueShape {
kind,
item_kind,
key_kind,
class,
})
}
pub fn walk_value(&mut self, kind: Kind) -> Result<(), Error> {
use Kind as K;
if let Some(width) = kind.fixed_width() {
return self.skip(width);
}
match kind {
K::String => {
let len = self.u16()? as usize;
self.skip(len)
}
K::Container | K::UnorderedContainer => {
let item_kind = self.kind()?;
self.sized_region(|cur| {
let count = cur.u32()?;
for _ in 0..count {
cur.walk_value(item_kind)?;
}
Ok(())
})
}
K::Map => {
let key_kind = self.kind()?;
let value_kind = self.kind()?;
self.sized_region(|cur| {
let count = cur.u32()?;
for _ in 0..count {
cur.walk_value(key_kind)?;
cur.walk_value(value_kind)?;
}
Ok(())
})
}
K::Struct | K::Embedded => {
if self.u32()? == 0 {
return Ok(());
}
self.sized_region(|cur| {
let count = cur.u16()?;
cur.walk_properties(count)
})
}
K::Optional => {
let item_kind = self.kind()?;
match self.bool()? {
true => self.walk_value(item_kind),
false => Ok(()),
}
}
_ => unreachable!("every kind without a fixed width is matched above"),
}
}
pub fn walk_object(&mut self) -> Result<(), Error> {
self.sized_region(|cur| {
cur.skip(4)?; let count = cur.u16()?;
cur.walk_properties(count)
})
}
fn walk_properties(&mut self, count: u16) -> Result<(), Error> {
for _ in 0..count {
self.skip(4)?; let kind = self.kind()?;
self.walk_value(kind)?;
}
Ok(())
}
pub fn sized_region<T>(
&mut self,
body: impl FnOnce(&mut Self) -> Result<T, Error>,
) -> Result<T, Error> {
let declared = self.u32()? as usize;
let start = self.pos;
let value = body(self)?;
let consumed = self.pos - start;
match consumed == declared {
true => Ok(value),
false => Err(Error::InvalidSize(declared as u64, consumed as u64)),
}
}
}
pub(crate) fn eof() -> Error {
Error::IOError(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
}