sfnt 0.12.0

A zero-allocation SFNT 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.

//! A zero-allocation SFNT parser.
//!
//! # Example
//!
//! ```
//! # extern crate sfnt;
//! use std::fs::{File};
//! use std::io::{Read};
//!
//! use sfnt::{Record, Sfnt, Tag};
//!
//! fn main() {
//!     // Read the font file into memory.
//!     let mut file = File::open("tests/resources/OpenSans-Italic.ttf").unwrap();
//!     let mut bytes = vec![];
//!     file.read_to_end(&mut bytes).unwrap();
//!
//!     // Parse the font file and find one of the tables in the font file.
//!     let sfnt = Sfnt::parse(&bytes).unwrap();
//!     let (record, bytes) = sfnt.find(b"head").unwrap();
//!
//!     assert_eq!(record, Record {
//!         tag: Tag(b"head"),
//!         checksum: 4165466467,
//!         offset: 316,
//!         length: 54,
//!     });
//!
//!     assert_eq!(bytes.len(), 54);
//! }
//! ```

#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs)]

#![no_std]

use core::ops::{Deref};

use tarrasque::*;

/// A `2.14` fixed-point number.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Fixed2_14(pub u16);

impl<'s> Extract<'s, ()> for Fixed2_14 {
    #[inline]
    fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
        stream.extract(()).map(Fixed2_14)
    }
}

impl Span for Fixed2_14 {
    const SPAN: usize = 2;
}

impl Into<f32> for Fixed2_14 {
    #[inline]
    fn into(self) -> f32 {
        f32::from(self.0) / 16384.0
    }
}

impl Into<f64> for Fixed2_14 {
    #[inline]
    fn into(self) -> f64 {
        f64::from(self.0) / 16384.0
    }
}

/// A `16.16` fixed-point number.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Fixed16_16(pub u32);

impl<'s> Extract<'s, ()> for Fixed16_16 {
    #[inline]
    fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
        stream.extract(()).map(Fixed16_16)
    }
}

impl Span for Fixed16_16 {
    const SPAN: usize = 4;
}

impl Into<f32> for Fixed16_16 {
    #[inline]
    fn into(self) -> f32 {
        Into::<f64>::into(self) as f32
    }
}

impl Into<f64> for Fixed16_16 {
    #[inline]
    fn into(self) -> f64 {
        f64::from(self.0) / 65536.0
    }
}

/// A four-byte tag.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Tag<'s>(pub &'s [u8]);

impl<'s> Extract<'s, ()> for Tag<'s> {
    fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
        stream.extract(4).map(Tag)
    }
}

impl<'s> Span for Tag<'s> {
    const SPAN: usize = 4;
}

impl<'s> Deref for Tag<'s> {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<'s> PartialEq<&[u8]> for Tag<'s> {
    fn eq(&self, other: &&[u8]) -> bool {
        self.0 == *other
    }
}

impl<'s> PartialEq<&[u8; 4]> for Tag<'s> {
    fn eq(&self, other: &&[u8; 4]) -> bool {
        self.0 == *other
    }
}

/// A number of seconds that have elapsed since `1904-01-01T00:00:00Z`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Timestamp(pub i64);

impl<'s> Extract<'s, ()> for Timestamp {
    #[inline]
    fn extract(stream: &mut Stream<'s>, _: ()) -> ExtractResult<'s, Self> {
        stream.extract(()).map(Timestamp)
    }
}

impl Span for Timestamp {
    const SPAN: usize = 8;
}

extract! {
    /// An SFNT file header.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Header[12] {
        /// The SFNT file version number.
        pub version: Fixed16_16 = [],
        /// The number of tables in the SFNT file.
        pub num_tables: u16 = [],
        /// The value of `(largest power of two <= num_tables) * 16`.
        pub search_range: u16 = [],
        /// The value of `log2(largest power of two <= num_tables)`.
        pub entry_selector: u16 = [],
        /// The value of `(num_tables * 16) - search_range`.
        pub range_shift: u16 = [],
    }
}

extract! {
    /// An SFNT file table record.
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Record<'s>[16] {
        /// The name of the table.
        pub tag: Tag<'s> = [],
        /// The checksum of the the table.
        pub checksum: u32 = [],
        /// The byte offset of the table in the SFNT file.
        pub offset: u32 = [],
        /// The byte length of the table.
        pub length: u32 = [],
    }
}

/// An SFNT file.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Sfnt<'s> {
    /// The bytes in this file.
    pub bytes: &'s [u8],
    /// The header of this file.
    pub header: Header,
    /// The table records for the tables in this file.
    pub records: View<'s, Record<'s>, ()>,
}

impl<'s> Sfnt<'s> {
    /// Parses the supplied SFNT file.
    #[inline]
    pub fn parse(bytes: &'s [u8]) -> ExtractResult<Self> {
        let mut stream = Stream(bytes);
        let header: Header = stream.extract(())?;
        let records = stream.extract((header.num_tables as usize, ()))?;

        let size = size(&records);
        if bytes.len() >= size {
            Ok(Sfnt { bytes, header, records })
        } else {
            Err(ExtractError::Insufficient(size))
        }
    }

    /// Returns the table in this file for the supplied table record.
    #[inline]
    pub fn get(&self, record: Record) -> &'s [u8] {
        let start = record.offset as usize;
        let end = start + record.length as usize;
        &self.bytes[start..end]
    }

    /// Returns the table in this file with the supplied tag.
    #[inline]
    pub fn find(&self, tag: &[u8]) -> Option<(Record<'s>, &'s [u8])> {
        let (index, _) = self.records.binary_search_bytes_by_key(&tag, |b| &b[..4])?;
        let record = self.records.extract(index).unwrap();
        Some((record, self.get(record)))
    }

    /// Returns an iterator over the tables in this file.
    #[inline]
    pub fn tables(self) -> TableIter<'s> {
        let records = self.records.iter();
        TableIter(self, records)
    }
}

/// An iterator over the tables in an SFNT file.
#[derive(Copy, Clone, Debug)]
pub struct TableIter<'s>(Sfnt<'s>, ViewIter<'s, Record<'s>, ()>);

impl<'s> Iterator for TableIter<'s> {
    type Item = (Record<'s>, &'s [u8]);

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.1.size_hint()
    }

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.1.next().map(|r| (r, self.0.get(r)))
    }
}

impl<'s> DoubleEndedIterator for TableIter<'s> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        self.1.next_back().map(|r| (r, self.0.get(r)))
    }
}

impl<'s> ExactSizeIterator for TableIter<'s> { }

/// Returns the SFNT checksum for the supplied bytes.
#[inline]
pub fn checksum(bytes: &[u8]) -> u32 {
    let (prefix, suffix) = bytes.split_at(bytes.len() - (bytes.len() % 4));
    let prefix = prefix.chunks(4).map(be_u32).fold(0u32, |a, i| a.wrapping_add(i));
    match *suffix {
        [] => prefix,
        [a] => prefix.wrapping_add(be_u32(&[a, 0, 0, 0])),
        [a, b] => prefix.wrapping_add(be_u32(&[a, b, 0, 0])),
        [a, b, c] => prefix.wrapping_add(be_u32(&[a, b, c, 0])),
        _ => unreachable!(),
    }
}

/// Returns the size of the SFNT file containing the supplied table records.
#[inline]
fn size<'s>(records: &View<'s, Record<'s>, ()>) -> usize {
    (0..records.len()).map(|i| {
        let bytes = records.get(i).unwrap();
        let offset = be_u32(&bytes[8..12]);
        let length = be_u32(&bytes[12..16]);
        (offset + length) as usize
    }).max().unwrap_or(0)
}