mod codec;
pub(crate) mod resolve;
#[cfg(test)]
mod tests;
use ltk_hash::HashWidth;
use crate::{path::ValueShape, property::Kind, Error};
#[derive(Debug, Default)]
pub struct HashWidths {
wide: Vec<usize>,
}
static NARROW: HashWidths = HashWidths::new();
impl HashWidths {
#[must_use]
pub const fn new() -> Self {
Self { wide: Vec::new() }
}
#[must_use]
pub fn wide_at(offset: usize) -> Self {
Self { wide: vec![offset] }
}
pub fn clear(&mut self) {
self.wide.clear();
}
#[must_use]
pub fn width_at(&self, offset: usize) -> HashWidth {
match !self.wide.is_empty() && self.wide.binary_search(&offset).is_ok() {
true => HashWidth::W8,
false => HashWidth::W4,
}
}
}
#[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,
base: usize,
numbering: Numbering,
widths: &'a HashWidths,
}
impl<'a> Cursor<'a> {
#[must_use]
pub fn new(buf: &'a [u8], numbering: Numbering) -> Self {
Self::with_widths(buf, numbering, &NARROW)
}
#[must_use]
pub fn with_widths(buf: &'a [u8], numbering: Numbering, widths: &'a HashWidths) -> Self {
Self {
buf,
pos: 0,
base: 0,
numbering,
widths,
}
}
pub fn sub(&mut self, n: usize) -> Result<Self, Error> {
let base = self.base + self.pos;
let buf = self.take(n)?;
Ok(Self {
buf,
pos: 0,
base,
numbering: self.numbering,
widths: self.widths,
})
}
#[must_use]
pub fn hash_width(&self) -> HashWidth {
self.widths.width_at(self.base + self.pos)
}
#[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::Hash => self.skip(self.hash_width().bytes()),
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 sub_value(&mut self, kind: Kind) -> Result<Self, Error> {
let start = self.pos;
if let Err(error) = self.skip_value(kind) {
self.pos = start;
return Err(error);
}
Ok(Self {
buf: &self.buf[start..self.pos],
pos: 0,
base: self.base + start,
numbering: self.numbering,
widths: self.widths,
})
}
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.item_kind()?), None, None)
}
K::Map => {
let key = ahead.key_kind()?;
let value = ahead.item_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 item_kind(&mut self) -> Result<Kind, Error> {
let kind = self.kind()?;
match kind.is_container() {
true => Err(Error::InvalidNesting(kind)),
false => Ok(kind),
}
}
pub fn key_kind(&mut self) -> Result<Kind, Error> {
let kind = self.kind()?;
match kind.is_valid_map_key() {
true => Ok(kind),
false => Err(Error::InvalidKeyType(kind)),
}
}
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::Hash => self.skip(self.hash_width().bytes()),
K::String => {
let len = self.u16()? as usize;
self.skip(len)
}
K::Container | K::UnorderedContainer => {
let item_kind = self.item_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.key_kind()?;
let value_kind = self.item_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.item_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))
}