cff 0.5.0

A zero-allocation CFF parser.
// Copyright 2018 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! CFF `DICT`s.

mod private;
mod top;

use core::str;

use tarrasque::*;

use crate::error::{MISMATCH, OPERAND};
pub use self::private::*;
pub use self::top::*;

/// A `DICT` number.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Number {
    /// An integer.
    Integer(i32),
    /// A floating-point number.
    Real(f32),
}

impl Number {
    /// Returns this number as a boolean if it is an integer in the inclusive
    /// range `[0, 1]`.
    #[inline]
    pub fn as_boolean<'s>(self) -> ExtractResult<'s, bool> {
        self.as_integer(0, 1).map(|i| i != 0)
    }

    /// Returns this number as a string ID if it is an integer in the inclusive
    /// range `[0, u16::max_value()]`.
    #[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)
    }

    /// Returns this number as an integer if it is an integer in the supplied
    /// inclusive range.
    #[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,
        }
    }
}

/// A list of `DICT` numbers.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Numbers<'s> {
    bytes: &'s [u8],
    numbers: usize,
}

impl<'s> Numbers<'s> {
    /// Returns the number of numbers in this list of numbers.
    #[inline]
    pub fn len(&self) -> usize {
        self.numbers
    }

    /// Returns whether there are no numbers in this list of numbers.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.numbers == 0
    }

    /// Returns an iterator over the numbers in this list of numbers.
    #[inline]
    pub fn numbers(&self) -> NumberIter<'s> {
        let stream = UnwrapStream(Stream(self.bytes));
        NumberIter { stream, remaining: self.numbers }
    }

    /// Returns the first number in this list of 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 })
    }
}

/// An iterator over the numbers in a list of `DICT` 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> { }

/// A `DICT` operator.
#[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! {
    /// A `DICT` entry.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Entry<'s> {
        /// The numbers for this entry.
        pub numbers: Numbers<'s> = [],
        /// The operator for this entry.
        pub operator: Operator = [],
    }
}

/// Extracts a floating-point `DICT` number from the supplied stream.
#[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);
    }

    // Safe because only ASCII characters are written to `real`.
    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);
    }
}