use bumpalo::Bump;
use super::sizes::{F32_BYTES, F64_BYTES};
use super::strings::Strings;
use super::{floats, lanes};
use crate::binary::{
Decoder, MAX_DEPTH, VARINT_BITS, VARINT_LAST_SHIFT, VARINT_MAX_SHIFT, VARINT_MORE,
VARINT_SHIFT, read_varint, unzigzag,
};
use crate::schema::{Schema, read_ref_value};
use crate::sync::Arc;
#[must_use]
pub fn read_frame<T: Schema>(bytes: &[u8]) -> Option<T> {
let mut u = Unpacker::open(bytes)?;
let v = T::unpack(&mut u)?;
u.at_end().then_some(v)
}
#[derive(Debug)]
pub struct Unpacker<'b> {
body: &'b [u8],
i: usize,
table: &'b [u8],
table_at: usize,
strings: Strings<'b>,
shared: Vec<Option<Arc<str>>>,
depth: usize,
}
impl<'b> Unpacker<'b> {
#[must_use]
pub fn open(bytes: &'b [u8]) -> Option<Self> {
let (table_len, trailer) = read_varint_back(bytes)?;
let body_end = bytes.len().checked_sub(trailer)?.checked_sub(table_len)?;
Some(Unpacker {
body: &bytes[..body_end],
i: 0,
table: &bytes[body_end..bytes.len() - trailer],
table_at: 0,
strings: Strings::new(),
shared: Vec::new(),
depth: 0,
})
}
#[must_use]
pub fn at_end(&self) -> bool {
self.i == self.body.len()
}
fn take(&mut self, len: usize) -> Option<&'b [u8]> {
let end = self.i.checked_add(len).filter(|&e| e <= self.body.len())?;
let out = &self.body[self.i..end];
self.i = end;
Some(out)
}
pub fn uint(&mut self) -> Option<u64> {
read_varint(self.body, &mut self.i)
}
pub fn int(&mut self) -> Option<i64> {
self.uint().map(unzigzag)
}
pub fn bool(&mut self) -> Option<bool> {
match self.take(1)? {
[0] => Some(false),
[1] => Some(true),
_ => None,
}
}
pub fn f64(&mut self) -> Option<f64> {
Some(f64::from_le_bytes(self.take(F64_BYTES)?.try_into().ok()?))
}
pub fn f32(&mut self) -> Option<f32> {
Some(f32::from_le_bytes(self.take(F32_BYTES)?.try_into().ok()?))
}
fn count_of(&mut self, min: usize) -> Option<usize> {
let n = usize::try_from(self.uint()?).ok()?;
(n <= (self.body.len() - self.i) / min.max(1)).then_some(n)
}
pub fn count(&mut self) -> Option<usize> {
self.count_of(1)
}
pub fn f64s(&mut self) -> Option<Vec<f64>> {
let (_, narrow, raw) = self.packed_floats(true)?;
Some(if narrow {
floats::widen(raw)
} else {
raw.as_chunks::<F64_BYTES>()
.0
.iter()
.map(|b| f64::from_le_bytes(*b))
.collect()
})
}
pub fn uints<T>(&mut self, of: impl Fn(u64) -> Option<T>) -> Option<Vec<T>> {
let (n, bits, raw) = self.packed_ints()?;
let mut out = Vec::with_capacity(n);
let mut ok = true;
lanes::unpack(raw, n, bits, |v| match of(v) {
Some(t) if ok => out.push(t),
_ => ok = false,
});
ok.then_some(out)
}
pub fn f32s(&mut self) -> Option<Vec<f32>> {
let (_, _, raw) = self.packed_floats(false)?;
Some(
raw.as_chunks::<F32_BYTES>()
.0
.iter()
.map(|b| f32::from_le_bytes(*b))
.collect(),
)
}
pub fn packed_ints(&mut self) -> Option<(usize, u32, &'b [u8])> {
let n = usize::try_from(self.uint()?).ok()?;
let bits = u32::from(*self.take(1)?.first()?);
if !(1..=u64::BITS).contains(&bits)
|| n > (self.body.len() - self.i) * u8::BITS as usize / bits as usize
{
return None;
}
let bytes = self.take(lanes::packed_bytes(n, bits)?)?;
Some((n, bits, bytes))
}
pub fn packed_floats(&mut self, wide: bool) -> Option<(usize, bool, &'b [u8])> {
let n = self.count()?;
let narrow = if wide { self.bool()? } else { true };
let width = if narrow { F32_BYTES } else { F64_BYTES };
Some((n, narrow, self.take(n.checked_mul(width)?)?))
}
pub fn str(&mut self) -> Option<&'b str> {
let i = self.string_index()?;
self.strings.get(i)
}
pub fn shared_str(&mut self) -> Option<Arc<str>> {
let i = self.string_index()?;
if self.shared.len() <= i {
self.shared.resize(i + 1, None);
}
let s = self.strings.get(i)?;
Some(self.shared[i].get_or_insert_with(|| s.into()).clone())
}
fn string_index(&mut self) -> Option<usize> {
let i = usize::try_from(self.uint()?).ok()?;
while self.strings.len() <= i {
let len = usize::try_from(read_varint(self.table, &mut self.table_at)?).ok()?;
let end = self
.table_at
.checked_add(len)
.filter(|&e| e <= self.table.len())?;
let s = crate::utf8::from_utf8(&self.table[self.table_at..end]).ok()?;
self.table_at = end;
self.strings.push(s);
}
Some(i)
}
pub fn bytes(&mut self) -> Option<&'b [u8]> {
let len = self.count()?;
self.take(len)
}
pub fn tagged<T: Schema>(&mut self) -> Option<T> {
let bytes = self.bytes()?;
let mut d = Decoder::body(bytes);
if let Some(v) = T::decode(&mut d)
&& d.at_end()
{
return Some(v);
}
let arena = Bump::new();
let mut any = Decoder::body(bytes);
let value = any.value_ref(&arena).ok()?;
any.at_end().then_some(())?;
read_ref_value(&value).ok()
}
pub fn begin(&mut self) -> Option<usize> {
let len = self.count()?;
self.depth += 1;
(self.depth <= MAX_DEPTH).then_some(self.i + len)
}
pub fn list(&mut self) -> Option<(usize, usize)> {
let n = self.count()?;
let len = self.count()?;
Some((n, self.i + len))
}
pub fn skip_to(&mut self, end: usize) -> Option<()> {
(self.i <= end && end <= self.body.len()).then(|| self.i = end)
}
#[must_use]
pub fn position(&self) -> usize {
self.i
}
#[must_use]
pub fn fork(&self, at: usize) -> Unpacker<'b> {
Unpacker {
body: self.body,
i: at,
table: self.table,
table_at: 0,
strings: Strings::new(),
shared: Vec::new(),
depth: self.depth,
}
}
#[must_use]
pub fn more(&self, end: usize) -> bool {
self.i < end
}
pub fn leave(&mut self, end: usize) -> Option<()> {
self.depth -= 1;
(self.i <= end).then(|| self.i = end)
}
}
fn read_varint_back(bytes: &[u8]) -> Option<(usize, usize)> {
let mut n = 0u64;
let mut shift = 0;
for (taken, b) in bytes.iter().rev().enumerate() {
if shift >= VARINT_MAX_SHIFT {
return None;
}
let bits = u64::from(*b) & VARINT_BITS;
if shift == VARINT_LAST_SHIFT && bits > 1 {
return None;
}
n |= bits << shift;
if b & VARINT_MORE == 0 {
return Some((usize::try_from(n).ok()?, taken + 1));
}
shift += VARINT_SHIFT;
}
None
}