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 type 2 charstring stacks.

#![allow(dead_code)]

use core::fmt::{Debug, Error, Formatter};

use tarrasque::{ExtractError, ExtractResult, Stream};

use crate::error::{OVERFLOW, UNDERFLOW};

/// The size of the charstring subroutine stack.
const SIZE: usize = 10;

/// A type 2 charstring subroutine stack.
#[derive(Copy, Clone)]
pub struct SubroutineStack<'s> {
    buffer: [Stream<'s>; SIZE],
    size: usize,
}

impl<'s> SubroutineStack<'s> {
    /// Constructs a new `SubroutineStack`.
    #[inline]
    pub fn new() -> Self {
        Self { buffer: [Stream(&[]); SIZE], size: 0 }
    }

    /// Returns the number of streams in this stack.
    #[inline]
    pub fn len(&self) -> usize {
        self.size
    }

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

    /// Pushes the supplied stream onto the top of this stack.
    #[inline]
    pub fn push(&mut self, value: Stream<'s>) -> ExtractResult<'s, ()> {
        if self.size < self.buffer.len() {
            self.buffer[self.size] = value;
            self.size += 1;
            Ok(())
        } else {
            Err(ExtractError::Code(OVERFLOW))
        }
    }

    /// Removes and returns the stream at the top of this stack.
    #[inline]
    pub fn pop(&mut self) -> ExtractResult<'s, Stream<'s>> {
        if !self.is_empty() {
            self.size -= 1;
            Ok(self.buffer[self.size])
        } else {
            Err(ExtractError::Code(UNDERFLOW))
        }
    }
}

impl<'s> Debug for SubroutineStack<'s> {
    #[inline]
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        f.debug_list().entries((0..self.size).map(|i| self.buffer[i])).finish()
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_subroutine_stack() {
        let streams = &[
            Stream(&[1]), Stream(&[2]), Stream(&[3]), Stream(&[4]), Stream(&[5]),
            Stream(&[6]), Stream(&[7]), Stream(&[8]), Stream(&[9]), Stream(&[10]),
        ];

        let mut stack = SubroutineStack::new();

        for stream in streams {
            stack.push(*stream).unwrap();
        }

        assert_eq!(stack.push(Stream(&[11])), Err(ExtractError::Code(OVERFLOW)));

        for stream in streams.iter().rev() {
            assert_eq!(stack.pop().unwrap(), *stream);
        }

        assert_eq!(stack.pop(), Err(ExtractError::Code(UNDERFLOW)));
    }
}