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 `INDEX`s.

mod strings;

use core::marker::{PhantomData};

use tarrasque::{Extract, ExtractResult, Stream, be_u16, be_u32, extract};

use super::{OffsetSize};
pub use self::strings::*;

/// Returns the first three bytes in the supplied slice as a big-endian `u32`.
#[inline]
fn be_u24(bytes: &[u8]) -> u32 {
    assert!(bytes.len() >= 3);
    let a = u32::from(bytes[0]) << 16;
    let b = u32::from(bytes[1]) << 8;
    let c = u32::from(bytes[2]);
    a + b + c
}

extract! {
    /// A list of offsets in an `INDEX`.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    struct Offsets<'s>(count: usize, size: usize) {
        size: usize = size,
        bytes: &'s [u8] = [count * size],
    }
}

impl<'s> Offsets<'s> {
    /// Returns the offset at the supplied index in this list of offsets.
    #[inline]
    fn get(&self, index: usize) -> usize {
        match self.size {
            1 => self.bytes[index] as usize,
            2 => be_u16(&self.bytes[index * 2..]) as usize,
            3 => be_u24(&self.bytes[index * 3..]) as usize,
            4 => be_u32(&self.bytes[index * 4..]) as usize,
            _ => unreachable!(),
        }
    }
}

/// An `INDEX`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Index<'s, T> where T: Extract<'s, ()> {
    count: usize,
    offsets: Offsets<'s>,
    bytes: &'s [u8],
    _marker: PhantomData<*const T>,
}

impl<'s, T> Index<'s, T> where T: Extract<'s, ()> {
    /// Returns the number of objects in this `INDEX`.
    #[inline]
    pub fn len(&self) -> usize {
        self.count
    }

    /// Returns whether there are no objects in this `INDEX`.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.count == 0
    }

    /// Returns the bytes for the object at the supplied index in this `INDEX`.
    #[inline]
    pub fn get(&self, index: usize) -> Option<&'s [u8]> {
        if index < self.count {
            let start = self.offsets.get(index) - 1;
            let end = self.offsets.get(index + 1) - 1;
            Some(&self.bytes[start..end])
        } else {
            None
        }
    }

    /// Parses the object at the supplied index in this `INDEX`.
    #[inline]
    pub fn parse_object(&self, index: usize) -> Option<ExtractResult<'s, T>> {
        self.get(index).map(|b| Stream(b).extract(()))
    }

    /// Returns an iterator over the objects in this `INDEX`.
    #[inline]
    pub fn objects(self) -> ObjectIter<'s, T> {
        let objects = self.len();
        ObjectIter { index: self, object: 0, objects }
    }
}

impl<'s, T> Extract<'s, ()> for Index<'s, T> where T: Extract<'s, ()> {
    #[inline]
    fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
        let count = stream.extract::<u16, _>(())? as usize;
        let size = stream.extract::<OffsetSize, _>(())?.0 as usize;
        let offsets: Offsets<'s> = stream.extract((count + 1, size))?;
        let bytes = stream.extract(offsets.get(count) - 1)?;
        Ok(Index { count, offsets, bytes, _marker: PhantomData })
    }
}

/// An iterator over the objects in an `INDEX`.
#[derive(Copy, Clone, Debug)]
pub struct ObjectIter<'s, T> where T: Extract<'s, ()> {
    index: Index<'s, T>,
    object: usize,
    objects: usize,
}

impl<'s, T> Iterator for ObjectIter<'s, T> where T: Extract<'s, ()> {
    type Item = ExtractResult<'s, T>;

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let size = self.objects - self.object;
        (size, Some(size))
    }

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.object != self.objects {
            self.object += 1;
            self.index.parse_object(self.object - 1)
        } else {
            None
        }
    }
}

impl<'s, T> DoubleEndedIterator for ObjectIter<'s, T> where T: Extract<'s, ()> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.object != self.objects {
            self.objects -= 1;
            self.index.parse_object(self.objects)
        } else {
            None
        }
    }
}

impl<'s, T> ExactSizeIterator for ObjectIter<'s, T> where T: Extract<'s, ()> { }