mod private;
mod top;
use core::str;
use tarrasque::*;
use crate::error::{MISMATCH, OPERAND};
pub use self::private::*;
pub use self::top::*;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Number {
Integer(i32),
Real(f32),
}
impl Number {
#[inline]
pub fn as_boolean<'s>(self) -> ExtractResult<'s, bool> {
self.as_integer(0, 1).map(|i| i != 0)
}
#[inline]
pub fn as_string_id<'s>(self) -> ExtractResult<'s, u16> {
self.as_integer(0, i32::from(u16::max_value())).map(|i| i as u16)
}
#[inline]
fn as_integer<'s>(self, min: i32, max: i32) -> ExtractResult<'s, i32> {
match self {
Number::Integer(i) if i >= min && i <= max => Ok(i),
_ => Err(ExtractError::Code(MISMATCH)),
}
}
}
impl Into<i32> for Number {
#[inline]
fn into(self) -> i32 {
match self {
Number::Integer(integer) => integer,
Number::Real(real) => real as i32,
}
}
}
impl Into<f32> for Number {
#[inline]
fn into(self) -> f32 {
match self {
Number::Integer(integer) => integer as f32,
Number::Real(real) => real,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Numbers<'s> {
bytes: &'s [u8],
numbers: usize,
}
impl<'s> Numbers<'s> {
#[inline]
pub fn len(&self) -> usize {
self.numbers
}
#[inline]
pub fn is_empty(&self) -> bool {
self.numbers == 0
}
#[inline]
pub fn numbers(&self) -> NumberIter<'s> {
let stream = UnwrapStream(Stream(self.bytes));
NumberIter { stream, remaining: self.numbers }
}
#[inline]
pub fn first(&self) -> ExtractResult<'s, Number> {
self.numbers().next().ok_or(ExtractError::Code(MISMATCH))
}
}
impl<'s> Extract<'s, ()> for Numbers<'s> {
#[inline]
fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
let start = &stream[..];
let mut numbers = 0;
while !stream.is_empty() && stream[0] >= 22 {
numbers += 1;
match stream.extract::<u8, _>(())? {
28 => { stream.extract::<&[u8], _>(2)?; },
29 => { stream.extract::<&[u8], _>(4)?; },
30 => stream.skip_while(|b| (b & 0x0F) != 0x0F),
b if b >= 32 && b <= 246 => { },
b if b >= 247 && b <= 254 => { stream.extract::<u8, _>(())?; },
_ => return Err(ExtractError::Code(OPERAND)),
}
}
let bytes = &start[..start.len() - stream.len()];
Ok(Numbers { bytes, numbers })
}
}
#[derive(Copy, Clone, Debug)]
pub struct NumberIter<'s> {
stream: UnwrapStream<'s>,
remaining: usize,
}
impl<'s> Iterator for NumberIter<'s> {
type Item = Number;
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let number = match self.stream.extract::<u8, _>(()) {
28 => Number::Integer(i32::from(self.stream.extract::<i16, _>(()))),
29 => Number::Integer(self.stream.extract::<i32, _>(())),
30 => Number::Real(extract_real(&mut self.stream)),
b if b >= 32 && b <= 246 => Number::Integer(i32::from(b) - 139),
b if b >= 247 && b <= 250 => {
let b0 = (i32::from(b) - 247) * 256;
let b1 = i32::from(self.stream.extract::<u8, _>(()));
Number::Integer(b0 + b1 + 108)
},
b if b >= 251 && b <= 254 => {
let b0 = (i32::from(b) - 251) * -256;
let b1 = i32::from(self.stream.extract::<u8, _>(()));
Number::Integer(b0 + b1 - 108)
},
_ => unreachable!(),
};
self.remaining -= 1;
Some(number)
}
}
impl<'s> ExactSizeIterator for NumberIter<'s> { }
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Operator(pub u16);
impl<'s> Extract<'s, ()> for Operator {
#[inline]
fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
let operator = u16::from(stream.extract::<u8, _>(())?);
if operator != 12 {
Ok(Operator(operator))
} else {
let low = u16::from(stream.extract::<u8, _>(())?);
Ok(Operator((operator << 8) + low))
}
}
}
extract! {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Entry<'s> {
pub numbers: Numbers<'s> = [],
pub operator: Operator = [],
}
}
#[inline]
fn extract_real(stream: &mut UnwrapStream) -> f32 {
let mut real = [0u8; 64];
let mut index = 0;
macro_rules! produce {
($byte:expr) => ({
real[index] = $byte;
index += 1;
});
}
macro_rules! consume {
($nybble:expr) => ({
match $nybble {
digit @ 0 ..= 9 => produce!(b'0' + digit),
0x0A => produce!(b'.'),
0x0B => produce!(b'e'),
0x0C => { produce!(b'e'); produce!(b'-'); },
0x0D => { },
0x0E => produce!(b'-'),
0x0F => break,
_ => unreachable!(),
}
});
}
while index < real.len() {
let byte: u8 = stream.extract(());
consume!((byte & 0xF0) >> 4);
consume!(byte & 0x0F);
}
let real = unsafe { str::from_utf8_unchecked(&real[..index]) };
real.parse().unwrap_or(0.0)
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! assert_real_eq {
($left:expr, $right:expr) => ({
let left = extract_real(&mut UnwrapStream(Stream($left)));
assert_eq!(left, $right);
});
}
#[test]
fn test_extract_real() {
assert_real_eq!(&[0x2A, 0x25, 0xFF], 2.25);
assert_real_eq!(&[0xE2, 0xA2, 0x5F], -2.25);
assert_real_eq!(&[0x0A, 0x14, 0x05, 0x41, 0xB3, 0xFF], 0.140_541e3);
assert_real_eq!(&[0x0A, 0x14, 0x05, 0x41, 0xC3, 0xFF], 0.140_541e-3);
assert_real_eq!(&[0x0A, 0x14, 0x05, 0x41, 0xB1, 0x1F], 0.140_541e11);
assert_real_eq!(&[0x0A, 0x14, 0x05, 0x41, 0xC1, 0x1F], 0.140_541e-11);
}
}