use super::{Error, Result};
use bytes::Bytes;
pub enum Len {
Plain(u64),
Special(u8),
}
pub struct Reader<'a> {
buf: &'a [u8],
pos: usize,
}
impl<'a> Reader<'a> {
pub fn new(buf: &'a [u8]) -> Reader<'a> {
Reader { buf, pos: 0 }
}
pub fn seek(&mut self, pos: usize) {
self.pos = pos;
}
pub fn corrupt(&self, what: &str) -> Error {
Error::Corrupt(format!("{what} at byte {}", self.pos))
}
pub fn byte(&mut self) -> Result<u8> {
let b = *self
.buf
.get(self.pos)
.ok_or_else(|| self.corrupt("unexpected end of file"))?;
self.pos += 1;
Ok(b)
}
pub fn take(&mut self, n: usize) -> Result<&'a [u8]> {
let end = self
.pos
.checked_add(n)
.ok_or_else(|| self.corrupt("length overflow"))?;
let slice = self
.buf
.get(self.pos..end)
.ok_or_else(|| self.corrupt("unexpected end of file"))?;
self.pos = end;
Ok(slice)
}
pub fn u32le(&mut self) -> Result<u32> {
Ok(u32::from_le_bytes(self.take(4)?.try_into().unwrap()))
}
pub fn u64le(&mut self) -> Result<u64> {
Ok(u64::from_le_bytes(self.take(8)?.try_into().unwrap()))
}
pub fn length(&mut self) -> Result<Len> {
let b = self.byte()?;
match b >> 6 {
0 => Ok(Len::Plain((b & 0x3f) as u64)),
1 => {
let lo = self.byte()?;
Ok(Len::Plain((((b & 0x3f) as u64) << 8) | lo as u64))
}
3 => Ok(Len::Special(b & 0x3f)),
_ => match b {
0x80 => Ok(Len::Plain(
u32::from_be_bytes(self.take(4)?.try_into().unwrap()) as u64,
)),
0x81 => Ok(Len::Plain(u64::from_be_bytes(
self.take(8)?.try_into().unwrap(),
))),
_ => Err(self.corrupt("unknown length encoding")),
},
}
}
pub fn count(&mut self) -> Result<usize> {
match self.length()? {
Len::Plain(n) => usize::try_from(n).map_err(|_| self.corrupt("length too large")),
Len::Special(_) => Err(self.corrupt("expected a length, found a string encoding")),
}
}
pub fn count_u64(&mut self) -> Result<u64> {
match self.length()? {
Len::Plain(n) => Ok(n),
Len::Special(_) => Err(self.corrupt("expected a length, found a string encoding")),
}
}
pub fn string(&mut self) -> Result<Bytes> {
match self.length()? {
Len::Plain(n) => {
let n = usize::try_from(n).map_err(|_| self.corrupt("string too large"))?;
Ok(Bytes::copy_from_slice(self.take(n)?))
}
Len::Special(0) => Ok(Bytes::from((self.byte()? as i8).to_string())),
Len::Special(1) => {
let v = i16::from_le_bytes(self.take(2)?.try_into().unwrap());
Ok(Bytes::from(v.to_string()))
}
Len::Special(2) => {
let v = i32::from_le_bytes(self.take(4)?.try_into().unwrap());
Ok(Bytes::from(v.to_string()))
}
Len::Special(3) => {
let compressed = self.count()?;
let expanded = self.count()?;
let block = self.take(compressed)?;
super::lzf::decompress(block, expanded)
.map(Bytes::from)
.ok_or_else(|| self.corrupt("malformed LZF block"))
}
Len::Special(other) => Err(Error::Corrupt(format!(
"unknown string encoding {other} at byte {}",
self.pos
))),
}
}
pub fn binary_double(&mut self) -> Result<f64> {
Ok(f64::from_le_bytes(self.take(8)?.try_into().unwrap()))
}
pub fn string_double(&mut self) -> Result<f64> {
match self.byte()? {
255 => Ok(f64::NEG_INFINITY),
254 => Ok(f64::INFINITY),
253 => Ok(f64::NAN),
n => {
let raw = self.take(n as usize)?;
std::str::from_utf8(raw)
.ok()
.and_then(|s| s.parse().ok())
.ok_or_else(|| self.corrupt("unparseable score"))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn six_and_fourteen_bit_lengths() {
let mut r = Reader::new(&[0x0a, 0x40 | 0x01, 0x2c]);
assert!(matches!(r.length(), Ok(Len::Plain(10))));
assert!(matches!(r.length(), Ok(Len::Plain(300))));
}
#[test]
fn wide_lengths_are_big_endian() {
let mut buf = vec![0x80];
buf.extend_from_slice(&70000u32.to_be_bytes());
buf.push(0x81);
buf.extend_from_slice(&5_000_000_000u64.to_be_bytes());
let mut r = Reader::new(&buf);
assert!(matches!(r.length(), Ok(Len::Plain(70000))));
assert!(matches!(r.length(), Ok(Len::Plain(5_000_000_000))));
}
#[test]
fn integer_encoded_strings_become_decimal() {
let mut buf = vec![0xc0, (-5i8) as u8, 0xc1];
buf.extend_from_slice(&(-300i16).to_le_bytes());
buf.push(0xc2);
buf.extend_from_slice(&100000i32.to_le_bytes());
let mut r = Reader::new(&buf);
assert_eq!(r.string().unwrap(), Bytes::from("-5"));
assert_eq!(r.string().unwrap(), Bytes::from("-300"));
assert_eq!(r.string().unwrap(), Bytes::from("100000"));
}
#[test]
fn truncation_is_an_error_not_a_panic() {
let mut r = Reader::new(&[0x05, b'a']);
assert!(r.string().is_err());
let mut r = Reader::new(&[]);
assert!(r.byte().is_err());
}
#[test]
fn non_finite_string_doubles() {
let mut r = Reader::new(&[255, 254, 253]);
assert_eq!(r.string_double().unwrap(), f64::NEG_INFINITY);
assert_eq!(r.string_double().unwrap(), f64::INFINITY);
assert!(r.string_double().unwrap().is_nan());
}
}