371 lines
11 KiB
Rust
371 lines
11 KiB
Rust
use std::borrow::Cow;
|
|
use std::io;
|
|
use std::marker::PhantomData;
|
|
|
|
use num_bigint::BigInt;
|
|
|
|
use crate::IOValue;
|
|
use crate::PlainValue;
|
|
use crate::SignedInteger;
|
|
use crate::Value;
|
|
use crate::ValueClass;
|
|
use crate::ValueImpl;
|
|
use crate::error;
|
|
use crate::error::io_eof;
|
|
use crate::iovalue;
|
|
use crate::owned;
|
|
use crate::shell;
|
|
use crate::reader::NextToken;
|
|
|
|
use super::constants::Tag;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct View<'de, Packed: AsRef<[u8]> + 'de> {
|
|
packed: Packed,
|
|
value_offset: usize, // annotation_offset is implicitly 0
|
|
value_end: usize,
|
|
phantom: PhantomData<&'de ()>,
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn at(packed: &[u8], i: usize) -> io::Result<u8> {
|
|
match packed.get(i) {
|
|
Some(v) => Ok(*v),
|
|
None => Err(io_eof()),
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn tag_at(packed: &[u8], i: usize) -> io::Result<Tag> {
|
|
Ok(Tag::try_from(at(packed, i)?)?)
|
|
}
|
|
|
|
fn skip_annotations(packed: &[u8], mut i: usize) -> io::Result<usize> {
|
|
loop {
|
|
if tag_at(packed, i)? == Tag::Annotation {
|
|
i = skip_value(packed, i + 1)?;
|
|
} else {
|
|
return Ok(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn varint(packed: &[u8], mut i: usize) -> io::Result<(u64, usize)> {
|
|
let mut shift = 0;
|
|
let mut acc: u64 = 0;
|
|
loop {
|
|
let v = at(packed, i)?;
|
|
i = i + 1;
|
|
if shift == 63 && v > 1 { Err(error::Error::Message("PackedReader length too long".to_string()))? }
|
|
acc |= ((v & 0x7f) as u64) << shift;
|
|
shift += 7;
|
|
if v & 0x80 == 0 { return Ok((acc, i)) }
|
|
if shift >= 70 { Err(error::Error::Message("PackedReader length too long".to_string()))? }
|
|
}
|
|
}
|
|
|
|
fn skip_value(packed: &[u8], mut i: usize) -> io::Result<usize> {
|
|
loop {
|
|
let next_i = match tag_at(packed, i)? {
|
|
Tag::False => i + 1,
|
|
Tag::True => i + 1,
|
|
Tag::Float => i + 5,
|
|
Tag::Double => i + 9,
|
|
Tag::End => Err(io::Error::new(io::ErrorKind::InvalidData, "Unexpected end tag"))?,
|
|
Tag::Annotation => {
|
|
i = skip_value(packed, i + 1)?;
|
|
continue;
|
|
}
|
|
Tag::Embedded => {
|
|
i = i + 1;
|
|
continue;
|
|
}
|
|
Tag::SmallInteger(_) => i + 1,
|
|
Tag::MediumInteger(n) => i + 1 + (n as usize),
|
|
Tag::SignedInteger | Tag::String | Tag:: ByteString | Tag:: Symbol => {
|
|
let (n, i) = varint(packed, i + 1)?;
|
|
i + (n as usize)
|
|
}
|
|
Tag::Record | Tag::Sequence | Tag::Set | Tag::Dictionary => {
|
|
i = i + 1;
|
|
while tag_at(packed, i)? != Tag::End {
|
|
i = skip_value(packed, i)?;
|
|
}
|
|
i + 1
|
|
}
|
|
};
|
|
return Ok(next_i);
|
|
}
|
|
}
|
|
|
|
impl<'de, Packed: AsRef<[u8]> + 'de> View<'de, Packed> {
|
|
pub fn new(packed: Packed) -> io::Result<Self> {
|
|
// println!("packed {:?}", &packed.as_ref());
|
|
let value_offset = skip_annotations(packed.as_ref(), 0)?;
|
|
let value_end = skip_value(packed.as_ref(), value_offset)?;
|
|
if value_end > packed.as_ref().len() { Err(io_eof())? }
|
|
Ok(View { packed, value_offset, value_end, phantom: PhantomData })
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn tag(&self) -> Tag {
|
|
tag_at(self.packed.as_ref(), self.value_offset).unwrap()
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn varint(&self) -> (u64, usize) {
|
|
varint(self.packed.as_ref(), self.value_offset + 1).unwrap()
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn sub(&self, offset: usize, len: usize) -> &[u8] {
|
|
let packed = self.packed.as_ref();
|
|
&packed[offset .. offset + len]
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn atom_chunk(&self) -> &[u8] {
|
|
let (n, i) = self.varint();
|
|
self.sub(i, n as usize)
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn signed_integer(&self, offset: usize, len: usize) -> SignedInteger {
|
|
let bs = self.sub(offset, len);
|
|
if (bs[0] & 0x80) == 0 {
|
|
// Positive or zero.
|
|
let mut i = 0;
|
|
while i < len && bs[i] == 0 { i += 1; }
|
|
if len - i <= 16 {
|
|
let mut v: u128 = 0;
|
|
for b in &bs[i..] { v = v << 8 | (*b as u128); }
|
|
SignedInteger::from(v)
|
|
} else {
|
|
SignedInteger::from(Cow::Owned(BigInt::from_bytes_be(num_bigint::Sign::Plus, &bs[i..])))
|
|
}
|
|
} else {
|
|
// Negative.
|
|
let mut i = 0;
|
|
while i < len && bs[i] == 0xff { i += 1; }
|
|
if len - i <= 16 {
|
|
let mut v: i128 = -1;
|
|
for b in &bs[i..] { v = v << 8 | (*b as i128); }
|
|
SignedInteger::from(v)
|
|
} else {
|
|
SignedInteger::from(Cow::Owned(BigInt::from_signed_bytes_be(&bs)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'de, Packed: AsRef<[u8]> + 'de> ValueImpl<IOValue> for View<'de, Packed> {
|
|
fn write(&self, w: &mut dyn crate::Writer, enc: &mut dyn crate::DomainEncode<IOValue>) -> io::Result<()> {
|
|
crate::write_value(w, self, enc)
|
|
}
|
|
|
|
fn value_clone(&self) -> PlainValue<'static, IOValue> where IOValue: 'static {
|
|
owned(View {
|
|
packed: self.packed.as_ref()[..self.value_end].to_owned(),
|
|
value_offset: self.value_offset,
|
|
value_end: self.value_end,
|
|
phantom: PhantomData,
|
|
})
|
|
}
|
|
|
|
fn value_class(&self) -> ValueClass {
|
|
match self.tag().into() {
|
|
Some(NextToken::Annotation) | None => unreachable!(),
|
|
Some(NextToken::Value(v)) => v,
|
|
}
|
|
}
|
|
|
|
fn as_boolean(&self) -> Option<bool> {
|
|
match self.tag() {
|
|
Tag::False => Some(false),
|
|
Tag::True => Some(true),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn as_float(&self) -> Option<f32> {
|
|
match self.tag() {
|
|
Tag::Float => Some(f32::from_be_bytes(self.sub(self.value_offset + 1, 4).try_into().unwrap())),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn as_double(&self) -> Option<f64> {
|
|
match self.tag() {
|
|
Tag::Double => Some(f64::from_be_bytes(self.sub(self.value_offset + 1, 8).try_into().unwrap())),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn is_signed_integer(&self) -> bool {
|
|
match self.tag() {
|
|
Tag::SmallInteger(_) => true,
|
|
Tag::MediumInteger(_) => true,
|
|
Tag::SignedInteger => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
fn as_signed_integer(&self) -> Option<SignedInteger> {
|
|
match self.tag() {
|
|
Tag::SmallInteger(v) => Some(v.into()),
|
|
Tag::MediumInteger(n) => Some(self.signed_integer(self.value_offset + 1, n as usize)),
|
|
Tag::SignedInteger => {
|
|
let (n, i) = self.varint();
|
|
Some(self.signed_integer(i, n as usize))
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn as_string(&self) -> Option<Cow<'_, str>> {
|
|
match self.tag() {
|
|
Tag::String => Some(Cow::Borrowed(std::str::from_utf8(self.atom_chunk()).unwrap())),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn as_bytestring(&self) -> Option<Cow<'_, [u8]>> {
|
|
match self.tag() {
|
|
Tag::ByteString => Some(Cow::Borrowed(self.atom_chunk())),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn as_symbol(&self) -> Option<Cow<'_, str>> {
|
|
match self.tag() {
|
|
Tag::Symbol => Some(Cow::Borrowed(std::str::from_utf8(self.atom_chunk()).unwrap())),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn is_record(&self) -> bool {
|
|
self.tag() == Tag::Record
|
|
}
|
|
|
|
fn label(&self) -> Value<'_, IOValue> {
|
|
if !self.is_record() { panic!("Not a record") }
|
|
shell(View::new(&self.packed.as_ref()[self.value_offset + 1 .. self.value_end]).unwrap())
|
|
}
|
|
|
|
fn is_sequence(&self) -> bool {
|
|
self.tag() == Tag::Sequence
|
|
}
|
|
|
|
fn len(&self) -> usize {
|
|
self.iter().count()
|
|
}
|
|
|
|
fn index(&self, i: usize) -> Value<'_, IOValue> {
|
|
self.iter().nth(i).unwrap()
|
|
}
|
|
|
|
fn iter(&self) -> Box<dyn Iterator<Item = Value<'_, IOValue>> + '_> {
|
|
let mut i = Box::new(ViewIterator::new(&self.packed.as_ref()[self.value_offset + 1 ..]));
|
|
match self.tag() {
|
|
Tag::Record => { i.next(); () }
|
|
Tag::Sequence => (),
|
|
Tag::Set => (),
|
|
_ => panic!("Not iterable"),
|
|
}
|
|
i
|
|
}
|
|
|
|
fn is_set(&self) -> bool {
|
|
self.tag() == Tag::Set
|
|
}
|
|
|
|
fn has(&self, v: &dyn ValueImpl<IOValue>) -> bool {
|
|
self.iter().find(|e| v == &**e).is_some()
|
|
}
|
|
|
|
fn is_dictionary(&self) -> bool {
|
|
self.tag() == Tag::Dictionary
|
|
}
|
|
|
|
fn get(&self, k: &dyn ValueImpl<IOValue>) -> Option<Value<'_, IOValue>> {
|
|
for (kk, vv) in self.entries() {
|
|
if &*kk == k { return Some(vv); }
|
|
}
|
|
None
|
|
}
|
|
|
|
fn entries(&self) -> Box<dyn Iterator<Item = (Value<'_, IOValue>, Value<'_, IOValue>)> + '_> {
|
|
if !self.is_dictionary() { panic!("Not a dictionary") }
|
|
Box::new(DictionaryAdapter(ViewIterator::new(&self.packed.as_ref()[self.value_offset + 1 ..])))
|
|
}
|
|
|
|
fn is_embedded(&self) -> bool {
|
|
self.tag() == Tag::Embedded
|
|
}
|
|
|
|
fn embedded(&self) -> Cow<'_, IOValue> {
|
|
let bs = self.packed.as_ref()[self.value_offset + 1 .. self.value_end].to_vec();
|
|
Cow::Owned(iovalue(View::new(bs).unwrap()))
|
|
}
|
|
|
|
fn annotations(&self) -> Option<Cow<'_, [IOValue]>> {
|
|
if self.value_offset == 0 {
|
|
None
|
|
} else {
|
|
let anns: Vec<IOValue> = AnnotationAdapter(ViewIterator::new(&self.packed.as_ref()[.. self.value_offset]))
|
|
.map(|ann| iovalue(ann.into_owned()))
|
|
.collect();
|
|
Some(Cow::Owned(anns))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ViewIterator<'de> {
|
|
packed: &'de [u8],
|
|
offset: usize,
|
|
}
|
|
|
|
impl<'de> ViewIterator<'de> {
|
|
pub fn new(packed: &'de [u8]) -> Self {
|
|
ViewIterator { packed, offset: 0 }
|
|
}
|
|
}
|
|
|
|
impl<'de> Iterator for ViewIterator<'de> {
|
|
type Item = Value<'de, IOValue>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let v = View::new(&self.packed[self.offset..]).ok()?;
|
|
if v.tag() == Tag::End { return None; }
|
|
self.offset += v.value_end;
|
|
Some(shell(v))
|
|
}
|
|
}
|
|
|
|
pub struct DictionaryAdapter<'de>(pub ViewIterator<'de>);
|
|
|
|
impl<'de> Iterator for DictionaryAdapter<'de> {
|
|
type Item = (Value<'de, IOValue>, Value<'de, IOValue>);
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let k = self.0.next()?;
|
|
let v = self.0.next()?;
|
|
Some((k, v))
|
|
}
|
|
}
|
|
|
|
pub struct AnnotationAdapter<'de>(pub ViewIterator<'de>);
|
|
|
|
impl<'de> Iterator for AnnotationAdapter<'de> {
|
|
type Item = Value<'de, IOValue>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if let Ok(Tag::Annotation) = tag_at(self.0.packed.as_ref(), self.0.offset) {
|
|
self.0.offset += 1;
|
|
self.0.next()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|