sfnt 0.7.0

A zero-allocation SFNT parser.
Documentation
// 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::{Sfnt};
//!
//! 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("head").unwrap();
//!
//!     println!("{:?}", record);
//!     // Record { tag: "head", checksum: 4165466467, offset: 316, length: 54 }
//!
//!     println!("{:?}", bytes.len());
//!     // 54
//! }
//! ```

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

#![no_std]

#[macro_use]
extern crate tarrasque;

use tarrasque::{Extract, ExtractError, ExtractResult, Stream, View, ViewIter, be_u32};

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

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

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<'a> Extract<'a, ()> for Fixed16_16 {
    #[inline]
    fn extract(stream: &mut Stream<'a>, _: ()) -> ExtractResult<'a, Self> {
        stream.extract(()).map(Fixed16_16)
    }
}

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 number of seconds that have elapsed since `1904-01-01T00:00:00Z`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Timestamp(pub i64);

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

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

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

/// Returns the index of the last byte of the table for the supplied table record.
#[inline]
fn tail(bytes: &[u8]) -> usize {
    let offset = be_u32(&bytes[8..12]);
    let length = be_u32(&bytes[12..16]);
    (offset + length) as usize
}

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

impl<'a> Sfnt<'a> {
    /// Parses the supplied SFNT file.
    #[inline]
    pub fn parse(bytes: &'a [u8]) -> ExtractResult<Self> {
        let mut stream = Stream(bytes);
        let header: Header = stream.extract(())?;
        let records: View<Record, _> = stream.extract((header.num_tables as usize, ()))?;
        let max = (0..records.len()).map(|i| tail(records.get(i).unwrap())).max().unwrap_or(0);
        if bytes.len() >= max {
            Ok(Sfnt { bytes, header, records })
        } else {
            Err(ExtractError::Insufficient(max - bytes.len()))
        }
    }

    /// Returns the table in this file for the supplied table record.
    #[inline]
    pub fn get(&self, record: Record) -> &'a [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: &str) -> Option<(Record<'a>, &'a [u8])> {
        let index = self.records.binary_search_bytes_by_key(&tag.as_bytes(), |b| &b[..4]);
        let record = index.map(|(i, _)| self.records.extract(i).unwrap());
        record.map(|r| (r, self.get(r)))
    }

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

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

impl<'a> Iterator for SfntIter<'a> {
    type Item = (Record<'a>, &'a [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<'a> DoubleEndedIterator for SfntIter<'a> {
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        self.1.next_back().map(|r| (r, self.0.get(r)))
    }
}

impl<'a> ExactSizeIterator for SfntIter<'a> { }

/// 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!(),
    }
}