<commit_before>extern crate combine;
extern crate combine_language;
extern crate mnemosyne as core;

use combine::*;
use combine_language::{ LanguageEnv
                      , LanguageDef
                      , Identifier
                      };
use combine::primitives::{ Stream
                         , Positioner
                         , SourcePosition
                         };

use core::semantic::*;
use core::semantic::annotations::{ Annotated
                                 , UnscopedState
                                 };
use core::semantic::ast::*;
use core::position::*;

type ParseFn<'a, I, T> = fn (&MnEnv<'a, I>, State<I>) -> ParseResult<T, I>;

type U = UnscopedState;

/// Wraps a parsing function with a language definition environment.
///
/// TODO: this could probably push identifiers to the symbol table here?
#[derive(Copy)]
struct MnParser<'a: 'b, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b {
        env: &'b MnEnv<'a, I>
      , parser: ParseFn<'a, I, T>
}

impl<'a, 'b, I, T> Clone for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    fn clone(&self) -> Self {
        MnParser { env: self.env , parser: self.parser }
    }
}

impl<'a, 'b, I, T> Parser for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    type Input = I;
    type Output = T;

    fn parse_state(&mut self, input: State<I>) -> ParseResult<T, I> {
        (self.parser)(self.env, input)
    }

}

struct MnEnv<'a, I>
where I: Stream<Item = char>
    , I::Item: Positioner<Position = SourcePosition> {
    env: LanguageEnv<'a, I>
}

impl <'a, I> std::ops::Deref for MnEnv<'a, I>
where I: Stream<Item=char> {
    type Target = LanguageEnv<'a, I>;
    fn deref(&self) -> &LanguageEnv<'a, I> { &self.env }
}

impl<'a, 'b, I> MnEnv<'a, I>
where I: Stream<Item=char>
    , I::Item: Positioner<Position = SourcePosition>
    , I::Range: 'b {

        /// Wrap a function into a MnParser with this environment
        fn parser<T>(&'b self, parser: ParseFn<'a, I, T>)
                    -> MnParser<'a, 'b, I, T> {
            MnParser { env: self, parser: parser }
        }

        fn parse_def(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_if(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_lambda(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_let(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_expr(&self, input: State<I>) -> ParseResult<Expr<'a, U>, I> {
            let pos = Position::from(input.position.clone());
            self.env
                .parens(choice([ self.parser(MnEnv::parse_def)
                               , self.parser(MnEnv::parse_if)
                               , self.parser(MnEnv::parse_lambda)
                               , self.parser(MnEnv::parse_let)
                               ]))
                .map(|f| Annotated::new(f, pos) )
                .parse_state(input)
        }

        fn expr(&'b self) -> MnParser<'a, 'b, I, Expr<'a, U>> {
            self.parser(MnEnv::parse_expr)
        }

        fn def(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }

        fn if_form(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }

        fn let_form(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }

        fn lambda(&'b self)-> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }
}
pub fn parse_module<N: ?Sized>(code: &str) -> Result<Vec<N>, ParseError<&str>>
where N: ASTNode + Sized {
    let alpha_ext = "+-*/<=>!?:$%_~^";
    let ops = "+-*/|=<>";
    let env = LanguageEnv::new(LanguageDef {
        ident: Identifier {
            start: letter().or(satisfy(|c| alpha_ext.contains(c)))
          , rest: alpha_num().or(satisfy(|c| alpha_ext.contains(c)))
          , reserved: [ "and"               , "begin"
                      , "case"              , "cond"
                      , "data"              , "define"
                      , "defn"              , "delay"
                      , "do"                , "else"
                      , "if"                , "lambda"
                      , "let"               , "let*"
                      , "letrec"            , "or"
                      , "quasiquote"        , "quote"
                      , "set!"              , "unquote"
                      , "unquote-splicing"
                      ].iter().map(|x| (*x).into()).collect()
        }
      , op: Identifier {
            start: satisfy(|c| ops.contains(c))
          , rest:  satisfy(|c| ops.contains(c))
          , reserved: [ "=>", "->", "\\", "|"].iter().map(|x| (*x).into())
                                                     .collect()
        }
      , comment_line: string(";").map(|_| ())
      , comment_start: string("#|").map(|_| ())
      , comment_end: string("|#").map(|_| ())
    });
    let env = MnEnv { env: env };

    env.white_space()
       .parse(code);

    unimplemented!()
}
<commit_msg>Added parser for calls<commit_after>extern crate combine;
extern crate combine_language;
extern crate mnemosyne as core;

use combine::*;
use combine_language::{ LanguageEnv
                      , LanguageDef
                      , Identifier
                      };
use combine::primitives::{ Stream
                         , Positioner
                         , SourcePosition
                         };

use core::semantic::*;
use core::semantic::annotations::{ Annotated
                                 , UnscopedState
                                 };
use core::semantic::ast::*;
use core::position::*;

type ParseFn<'a, I, T> = fn (&MnEnv<'a, I>, State<I>) -> ParseResult<T, I>;

type U = UnscopedState;

/// Wraps a parsing function with a language definition environment.
///
/// TODO: this could probably push identifiers to the symbol table here?
#[derive(Copy)]
struct MnParser<'a: 'b, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b {
        env: &'b MnEnv<'a, I>
      , parser: ParseFn<'a, I, T>
}

impl<'a, 'b, I, T> Clone for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    fn clone(&self) -> Self {
        MnParser { env: self.env , parser: self.parser }
    }
}

impl<'a, 'b, I, T> Parser for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    type Input = I;
    type Output = T;

    fn parse_state(&mut self, input: State<I>) -> ParseResult<T, I> {
        (self.parser)(self.env, input)
    }

}

struct MnEnv<'a, I>
where I: Stream<Item = char>
    , I::Item: Positioner<Position = SourcePosition> {
    env: LanguageEnv<'a, I>
}

impl <'a, I> std::ops::Deref for MnEnv<'a, I>
where I: Stream<Item=char> {
    type Target = LanguageEnv<'a, I>;
    fn deref(&self) -> &LanguageEnv<'a, I> { &self.env }
}

impl<'a, 'b, I> MnEnv<'a, I>
where I: Stream<Item=char>
    , I::Item: Positioner<Position = SourcePosition>
    , I::Range: 'b {

        /// Wrap a function into a MnParser with this environment
        fn parser<T>(&'b self, parser: ParseFn<'a, I, T>)
                    -> MnParser<'a, 'b, I, T> {
            MnParser { env: self, parser: parser }
        }

        fn parse_def(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_if(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_lambda(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_let(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_call(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            let pos = Position::from(input.position.clone());
            self.env.identifier::<'b>()
                    .and(many(self.parser(MnEnv::parse_expr)))
                    .map(|(name, args)| Form::Call {
                            fun: Positional { pos: pos, value: name }
                          , body: args
                    })
                    .parse_state(input)
        }

        fn parse_expr(&self, input: State<I>) -> ParseResult<Expr<'a, U>, I> {
            let pos = Position::from(input.position.clone());
            self.env
                .parens(choice([ self.parser(MnEnv::parse_def)
                               , self.parser(MnEnv::parse_if)
                               , self.parser(MnEnv::parse_lambda)
                               , self.parser(MnEnv::parse_let)
                               , self.parser(MnEnv::parse_call)
                               ]))
                .map(|f| Annotated::new(f, pos) )
                .parse_state(input)
        }

        fn expr(&'b self) -> MnParser<'a, 'b, I, Expr<'a, U>> {
            self.parser(MnEnv::parse_expr)
        }

        fn def(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }

        fn if_form(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }

        fn let_form(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }

        fn lambda(&'b self)-> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }
}
pub fn parse_module<N: ?Sized>(code: &str) -> Result<Vec<N>, ParseError<&str>>
where N: ASTNode + Sized {
    let alpha_ext = "+-*/<=>!?:$%_~^";
    let ops = "+-*/|=<>";
    let env = LanguageEnv::new(LanguageDef {
        ident: Identifier {
            start: letter().or(satisfy(|c| alpha_ext.contains(c)))
          , rest: alpha_num().or(satisfy(|c| alpha_ext.contains(c)))
          , reserved: [ "and"               , "begin"
                      , "case"              , "cond"
                      , "data"              , "define"
                      , "defn"              , "delay"
                      , "do"                , "else"
                      , "if"                , "lambda"
                      , "let"               , "let*"
                      , "letrec"            , "or"
                      , "quasiquote"        , "quote"
                      , "set!"              , "unquote"
                      , "unquote-splicing"
                      ].iter().map(|x| (*x).into()).collect()
        }
      , op: Identifier {
            start: satisfy(|c| ops.contains(c))
          , rest:  satisfy(|c| ops.contains(c))
          , reserved: [ "=>", "->", "\\", "|"].iter().map(|x| (*x).into())
                                                     .collect()
        }
      , comment_line: string(";").map(|_| ())
      , comment_start: string("#|").map(|_| ())
      , comment_end: string("|#").map(|_| ())
    });
    let env = MnEnv { env: env };

    env.white_space()
       .parse(code);

    unimplemented!()
}
<|endoftext|>


<commit_before>use cortex_m;
use stm32f0x2::{NVIC, RCC, GPIOA, USART1 as UART1};
use stm32f0x2::interrupt::*;

static mut DATA_UART1: u16 = 0;

pub enum NBITS {
    _8bits,
    _9bits,
}

pub enum STOPBITS {
    _0b5,
    _1b,
    _1b5,
    _2b,
}

pub enum PARITY {
    /// No Parity
    None,
    /// Even Parity
    Even,
    /// Odd Parity
    Odd,
}

pub fn init(baudrate: u32, nbits: NBITS, nbstopbits: STOPBITS, parity: PARITY) {
    cortex_m::interrupt::free(|cs| {
        let rcc = RCC.borrow(cs);
        let gpioa = GPIOA.borrow(cs);
        let uart1 = UART1.borrow(cs);
        let nvic = NVIC.borrow(cs);

        // Enable GPIOA Clock
        rcc.ahbenr.write(|w| w.iopaen().enabled());
        // Enable USART1 Clock
        rcc.apb2enr.write(|w| w.usart1en().enabled());

        // Configure PA9/PA10 Alternate Function 1 -> USART1
        gpioa.ospeedr.write(|w| {
            w.ospeedr9().high_speed().ospeedr10().high_speed()
        });
        gpioa.pupdr.write(
            |w| w.pupdr9().pull_up().pupdr10().pull_up(),
        );
        gpioa.afrh.write(|w| w.afrh9().af1().afrh10().af1());
        gpioa.moder.write(
            |w| w.moder9().alternate().moder10().alternate(),
        );
        gpioa.otyper.write(
            |w| w.ot9().push_pull().ot10().push_pull(),
        );

        // Configure UART : Word lenght
        match nbits {
            NBITS::_8bits => {
                uart1.cr1.modify(|_, w| w.m()._8bits());
            }
            NBITS::_9bits => {
                uart1.cr1.modify(|_, w| w.m()._9bits());
            }
        }
        // Configure UART : Parity
        match parity {
            PARITY::None => {
                uart1.cr1.modify(|_, w| w.pce().disabled());
            }
            PARITY::Even => {
                uart1.cr1.modify(|_, w| w.pce().enabled());
                uart1.cr1.modify(|_, w| w.ps().even());
            }
            PARITY::Odd => {
                uart1.cr1.modify(|_, w| w.pce().enabled());
                uart1.cr1.modify(|_, w| w.ps().odd());
            }
        }
        // Configure UART : Transfert Direction - Oversampling - RX Interrupt
        uart1.cr1.modify(|_, w| {
            w.te()
                .enabled()
                .re()
                .enabled()
                .over8()
                .over8()
                .rxneie()
                .enabled()
        });
        // Configure UART : 1 stop bit
        match nbstopbits {
            STOPBITS::_0b5 => {
                uart1.cr2.modify(|_, w| w.stop().half_stop());
            }
            STOPBITS::_1b => {
                uart1.cr2.modify(|_, w| w.stop()._1stop());
            }
            STOPBITS::_1b5 => {
                uart1.cr2.modify(|_, w| w.stop()._1half_stop());
            }
            STOPBITS::_2b => {
                uart1.cr2.modify(|_, w| w.stop()._2stop());
            }
        }

        // Configure UART : disable hardware flow control - Overrun interrupt
        uart1.cr3.write(|w| {
            w.rtse()
                .disabled()
                .ctse()
                .disabled()
                .ctsie()
                .disabled()
                .ovrdis()
                .disabled()
        });
        // Configure UART : baudrate
        uart1.brr.write(|w| {
            w.div_fraction().bits(
                (48000000 / (baudrate / 2)) as u8 & 0x0F,
            )
        });
        uart1.brr.write(|w| {
            w.div_mantissa().bits(
                ((48000000 / (baudrate / 2)) >> 4) as u16,
            )
        });
        // Configure UART : Asynchronous mode
        uart1.cr2.modify(
            |_, w| w.linen().disabled().clken().disabled(),
        );
        // UART1 enabled
        uart1.cr1.modify(|_, w| w.ue().enabled());
        nvic.enable(Interrupt::USART1);
        nvic.clear_pending(Interrupt::USART1);
    })
}

pub fn send(byte: u16) {
    cortex_m::interrupt::free(|cs| {
        let uart1 = UART1.borrow(cs);
        uart1.tdr.write(|w| w.tdr().bits(byte));
    })
}

pub fn transmit_complete() -> bool {
    cortex_m::interrupt::free(|cs| {
        let uart1 = UART1.borrow(cs);
        if uart1.isr.read().tc().bit_is_set() {
            uart1.icr.write(|w| w.tccf().clear_bit());
            return true;
        } else {
            return false;
        }
    })
}

fn receive_callback() {
    cortex_m::interrupt::free(|cs| {
        let uart = UART1.borrow(cs);
        unsafe {
            DATA_UART1 = uart.rdr.read().rdr().bits();
        }
    })
}

interrupt!(USART1, receive);

pub fn receive() {
    cortex_m::interrupt::free(|cs| {
        let uart = UART1.borrow(cs);
        if uart.isr.read().rxne().bit_is_set() {
            receive_callback();
        }
    })
}
<commit_msg>Typo.<commit_after>use cortex_m;
use stm32f0x2::{NVIC, RCC, GPIOA, USART1 as UART1};
use stm32f0x2::interrupt::*;

static mut DATA_UART1: u16 = 0;

pub enum NBITS {
    _8bits,
    _9bits,
}

pub enum STOPBITS {
    _0b5,
    _1b,
    _1b5,
    _2b,
}

pub enum PARITY {
    /// No Parity
    None,
    /// Even Parity
    Even,
    /// Odd Parity
    Odd,
}

pub fn init(baudrate: u32, nbits: NBITS, nbstopbits: STOPBITS, parity: PARITY) {
    cortex_m::interrupt::free(|cs| {
        let rcc = RCC.borrow(cs);
        let gpioa = GPIOA.borrow(cs);
        let uart1 = UART1.borrow(cs);
        let nvic = NVIC.borrow(cs);

        // Enable GPIOA Clock
        rcc.ahbenr.write(|w| w.iopaen().enabled());
        // Enable USART1 Clock
        rcc.apb2enr.write(|w| w.usart1en().enabled());

        // Configure PA9/PA10 Alternate Function 1 -> USART1
        gpioa.ospeedr.write(|w| {
            w.ospeedr9().high_speed().ospeedr10().high_speed()
        });
        gpioa.pupdr.write(
            |w| w.pupdr9().pull_up().pupdr10().pull_up(),
        );
        gpioa.afrh.write(|w| w.afrh9().af1().afrh10().af1());
        gpioa.moder.write(
            |w| w.moder9().alternate().moder10().alternate(),
        );
        gpioa.otyper.write(
            |w| w.ot9().push_pull().ot10().push_pull(),
        );

        // Configure UART : Word length
        match nbits {
            NBITS::_8bits => {
                uart1.cr1.modify(|_, w| w.m()._8bits());
            }
            NBITS::_9bits => {
                uart1.cr1.modify(|_, w| w.m()._9bits());
            }
        }
        // Configure UART : Parity
        match parity {
            PARITY::None => {
                uart1.cr1.modify(|_, w| w.pce().disabled());
            }
            PARITY::Even => {
                uart1.cr1.modify(|_, w| w.pce().enabled());
                uart1.cr1.modify(|_, w| w.ps().even());
            }
            PARITY::Odd => {
                uart1.cr1.modify(|_, w| w.pce().enabled());
                uart1.cr1.modify(|_, w| w.ps().odd());
            }
        }
        // Configure UART : Transfert Direction - Oversampling - RX Interrupt
        uart1.cr1.modify(|_, w| {
            w.te()
                .enabled()
                .re()
                .enabled()
                .over8()
                .over8()
                .rxneie()
                .enabled()
        });
        // Configure UART : 1 stop bit
        match nbstopbits {
            STOPBITS::_0b5 => {
                uart1.cr2.modify(|_, w| w.stop().half_stop());
            }
            STOPBITS::_1b => {
                uart1.cr2.modify(|_, w| w.stop()._1stop());
            }
            STOPBITS::_1b5 => {
                uart1.cr2.modify(|_, w| w.stop()._1half_stop());
            }
            STOPBITS::_2b => {
                uart1.cr2.modify(|_, w| w.stop()._2stop());
            }
        }

        // Configure UART : disable hardware flow control - Overrun interrupt
        uart1.cr3.write(|w| {
            w.rtse()
                .disabled()
                .ctse()
                .disabled()
                .ctsie()
                .disabled()
                .ovrdis()
                .disabled()
        });
        // Configure UART : baudrate
        uart1.brr.write(|w| {
            w.div_fraction().bits(
                (48000000 / (baudrate / 2)) as u8 & 0x0F,
            )
        });
        uart1.brr.write(|w| {
            w.div_mantissa().bits(
                ((48000000 / (baudrate / 2)) >> 4) as u16,
            )
        });
        // Configure UART : Asynchronous mode
        uart1.cr2.modify(
            |_, w| w.linen().disabled().clken().disabled(),
        );
        // UART1 enabled
        uart1.cr1.modify(|_, w| w.ue().enabled());
        nvic.enable(Interrupt::USART1);
        nvic.clear_pending(Interrupt::USART1);
    })
}

pub fn send(byte: u16) {
    cortex_m::interrupt::free(|cs| {
        let uart1 = UART1.borrow(cs);
        uart1.tdr.write(|w| w.tdr().bits(byte));
    })
}

pub fn transmit_complete() -> bool {
    cortex_m::interrupt::free(|cs| {
        let uart1 = UART1.borrow(cs);
        if uart1.isr.read().tc().bit_is_set() {
            uart1.icr.write(|w| w.tccf().clear_bit());
            return true;
        } else {
            return false;
        }
    })
}

fn receive_callback() {
    cortex_m::interrupt::free(|cs| {
        let uart = UART1.borrow(cs);
        unsafe {
            DATA_UART1 = uart.rdr.read().rdr().bits();
        }
    })
}

interrupt!(USART1, receive);

pub fn receive() {
    cortex_m::interrupt::free(|cs| {
        let uart = UART1.borrow(cs);
        if uart.isr.read().rxne().bit_is_set() {
            receive_callback();
        }
    })
}
<|endoftext|>


<commit_before>use super::math::Vec2;
use super::shape::shape::Shape;

#[derive(Clone)]
pub enum BodyType {
	StaticBody,
	KinematicBody,
	DynamicBody
}

pub struct BodyDef {
	pub shape: Shape,
	pub body_type: BodyType,
	pub position: Vec2,
	pub velocity: Vec2,
	pub restitution: f32,
	pub mass: f32
}

#[derive(Clone)]
pub struct Body {
	pub id: usize,
	pub shape: Shape,
	pub body_type: BodyType,
	pub position: Vec2,
	pub velocity: Vec2,
	pub restitution: f32,
	pub mass: f32
}

impl Body {
	pub fn new(id: usize, body_def: BodyDef) -> Body {
		return Body{ id: id, shape: body_def.shape, body_type: body_def.body_type, position: body_def.position, velocity: body_def.velocity, restitution: body_def.restitution, mass: body_def.mass}
	}
	pub fn integrate(&mut self, timeStep: f32) {
		if self.mass <= 0.0 {
			return;
		}
		let inv_mass = 1.0 / self.mass;
		if !inv_mass.is_nan() && !inv_mass.is_infinite() && inv_mass > 0.0 && timeStep > 0.0 {
			//Add gravity to the object;
			let force_accum = Vec2::new(0.0, 10.0);

			let total_accel = force_accum.multiply(inv_mass);
			self.velocity = self.velocity + total_accel.multiply(timeStep);
			self.position = self.position + self.velocity.multiply(timeStep);
		}
	}
}
<commit_msg>Adding gravity scale<commit_after>use super::math::Vec2;
use super::shape::shape::Shape;

#[derive(Clone)]
pub enum BodyType {
	StaticBody,
	KinematicBody,
	DynamicBody
}

pub struct BodyDef {
	pub shape: Shape,
	pub body_type: BodyType,
	pub position: Vec2,
	pub velocity: Vec2,
	pub restitution: f32,
	pub mass: f32,
	pub gravity_scale: f32
}

#[derive(Clone)]
pub struct Body {
	pub id: usize,
	pub shape: Shape,
	pub body_type: BodyType,
	pub position: Vec2,
	pub velocity: Vec2,
	pub restitution: f32,
	pub mass: f32,
	pub gravity_scale: f32
}

impl Body {
	pub fn new(id: usize, body_def: BodyDef) -> Body {
		return Body{ id: id, shape: body_def.shape, body_type: body_def.body_type, position: body_def.position,
			velocity: body_def.velocity, restitution: body_def.restitution, mass: body_def.mass, gravity_scale: body_def.gravity_scale};
	}
	pub fn integrate(&mut self, timeStep: f32) {
		if self.mass <= 0.0 {
			return;
		}
		let inv_mass = 1.0 / self.mass;
		if !inv_mass.is_nan() && !inv_mass.is_infinite() && inv_mass > 0.0 && timeStep > 0.0 {
			//Add gravity to the object;
			let force_accum = Vec2::new(0.0, 10.0).multiply(self.gravity_scale);

			let total_accel = force_accum.multiply(inv_mass);
			self.velocity = self.velocity + total_accel.multiply(timeStep);
			self.position = self.position + self.velocity.multiply(timeStep);
		}
	}
}
<|endoftext|>


<commit_before>//! C API for mp4parse module.
//!
//! Parses ISO Base Media Format aka video/mp4 streams.
//!
//! # Examples
//!
//! ```rust
//! extern crate mp4parse;
//!
//! // Minimal valid mp4 containing no tracks.
//! let data = b"\0\0\0\x0cftypmp42";
//!
//! let context = mp4parse::mp4parse_new();
//! unsafe {
//!     let rv = mp4parse::mp4parse_read(context, data.as_ptr(), data.len());
//!     assert_eq!(0, rv);
//!     mp4parse::mp4parse_free(context);
//! }
//! ```

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std;
use std::io::Cursor;

// Symbols we need from our rust api.
use MediaContext;
use read_box;
use Error;

/// Allocate an opaque rust-side parser context.
#[no_mangle]
pub extern "C" fn mp4parse_new() -> *mut MediaContext {
    let context = Box::new(MediaContext::new());
    unsafe {
        // transmute is unsafe, but context is always valid.
        std::mem::transmute(context)
    }
}

/// Free a rust-side parser context.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_free(context: *mut MediaContext) {
    assert!(!context.is_null());
    let _: Box<MediaContext> = std::mem::transmute(context);
}

/// Feed a buffer through `read_box()` with the given rust-side
/// parser context, returning the number of detected tracks.
///
/// This is safe to call with NULL arguments but will crash
/// if given invalid pointers, as is usual for C.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *const u8, size: usize) -> i32 {
    // Validate arguments from C.
    if context.is_null() {
        return -1;
    }
    if buffer.is_null() || size < 8 {
        return -1;
    }

    let mut context: &mut MediaContext = &mut *context;

    // Wrap the buffer we've been give in a slice.
    let b = std::slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    // Parse in a subthread to catch any panics.
    let task = std::thread::spawn(move || {
        loop {
            match read_box(&mut c, &mut context) {
                Ok(_) => {},
                Err(Error::UnexpectedEOF) => { break },
                Err(e) => { panic!(e); },
            }
        }
        // Make sure the track count fits in an i32 so we can use
        // negative values for failure.
        assert!(context.tracks.len() < std::i32::MAX as usize);
        context.tracks.len() as i32
    });
    task.join().unwrap_or(-1)
}

#[test]
fn new_context() {
    let context = mp4parse_new();
    assert!(!context.is_null());
    unsafe { mp4parse_free(context); }
}

#[test]
#[should_panic(expected = "assertion failed")]
fn free_null_context() {
    unsafe { mp4parse_free(std::ptr::null_mut()); }
}

#[test]
fn arg_validation() {
    let null_buffer = std::ptr::null();
    let null_context = std::ptr::null_mut();

    let context = mp4parse_new();
    assert!(!context.is_null());

    let buffer = vec![0u8; 8];

    unsafe {
        assert_eq!(-1, mp4parse_read(null_context, null_buffer, 0));
        assert_eq!(-1, mp4parse_read(context, null_buffer, 0));
    }

    for size in 0..buffer.len() {
        println!("testing buffer length {}", size);
        unsafe {
            assert_eq!(-1, mp4parse_read(context, buffer.as_ptr(), size));
        }
    }

    unsafe { mp4parse_free(context); }
}
<commit_msg>Use Box::{into_raw, from_raw} instead of mem::transmute.<commit_after>//! C API for mp4parse module.
//!
//! Parses ISO Base Media Format aka video/mp4 streams.
//!
//! # Examples
//!
//! ```rust
//! extern crate mp4parse;
//!
//! // Minimal valid mp4 containing no tracks.
//! let data = b"\0\0\0\x0cftypmp42";
//!
//! let context = mp4parse::mp4parse_new();
//! unsafe {
//!     let rv = mp4parse::mp4parse_read(context, data.as_ptr(), data.len());
//!     assert_eq!(0, rv);
//!     mp4parse::mp4parse_free(context);
//! }
//! ```

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std;
use std::io::Cursor;

// Symbols we need from our rust api.
use MediaContext;
use read_box;
use Error;

/// Allocate an opaque rust-side parser context.
#[no_mangle]
pub extern "C" fn mp4parse_new() -> *mut MediaContext {
    let context = Box::new(MediaContext::new());
    Box::into_raw(context)
}

/// Free a rust-side parser context.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_free(context: *mut MediaContext) {
    assert!(!context.is_null());
    let _ = Box::from_raw(context);
}

/// Feed a buffer through `read_box()` with the given rust-side
/// parser context, returning the number of detected tracks.
///
/// This is safe to call with NULL arguments but will crash
/// if given invalid pointers, as is usual for C.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *const u8, size: usize) -> i32 {
    // Validate arguments from C.
    if context.is_null() {
        return -1;
    }
    if buffer.is_null() || size < 8 {
        return -1;
    }

    let mut context: &mut MediaContext = &mut *context;

    // Wrap the buffer we've been give in a slice.
    let b = std::slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    // Parse in a subthread to catch any panics.
    let task = std::thread::spawn(move || {
        loop {
            match read_box(&mut c, &mut context) {
                Ok(_) => {},
                Err(Error::UnexpectedEOF) => { break },
                Err(e) => { panic!(e); },
            }
        }
        // Make sure the track count fits in an i32 so we can use
        // negative values for failure.
        assert!(context.tracks.len() < std::i32::MAX as usize);
        context.tracks.len() as i32
    });
    task.join().unwrap_or(-1)
}

#[test]
fn new_context() {
    let context = mp4parse_new();
    assert!(!context.is_null());
    unsafe { mp4parse_free(context); }
}

#[test]
#[should_panic(expected = "assertion failed")]
fn free_null_context() {
    unsafe { mp4parse_free(std::ptr::null_mut()); }
}

#[test]
fn arg_validation() {
    let null_buffer = std::ptr::null();
    let null_context = std::ptr::null_mut();

    let context = mp4parse_new();
    assert!(!context.is_null());

    let buffer = vec![0u8; 8];

    unsafe {
        assert_eq!(-1, mp4parse_read(null_context, null_buffer, 0));
        assert_eq!(-1, mp4parse_read(context, null_buffer, 0));
    }

    for size in 0..buffer.len() {
        println!("testing buffer length {}", size);
        unsafe {
            assert_eq!(-1, mp4parse_read(context, buffer.as_ptr(), size));
        }
    }

    unsafe { mp4parse_free(context); }
}
<|endoftext|>


<commit_before>//   Copyright Colin Sherratt 2014
//
//   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.

use core::iter::Range;
use super::{MapVertex, Quad};
use super::generators::{SharedVertex, IndexedPolygon};

/// A perfect cube, centered at (0, 0, 0) with each face starting at 1/-1 away from the origin
pub struct Cube {
    range: Range<uint>
}

impl Cube {
    /// create a new cube generator
    pub fn new() -> Cube {
        Cube { range: range(0, 6) }
    }

    fn vert(&self, idx: uint) -> (f32, f32, f32) {
        let x = if idx & 4 == 4 { 1.} else { -1. };
        let y = if idx & 2 == 2 { 1.} else { -1. };
        let z = if idx & 1 == 1 { 1.} else { -1. };
        (x, y, z)
    }

    fn face_indexed(&self, idx: uint) -> Quad<uint> {
        match idx {
            0 => Quad::new(0b000, 0b001, 0b011, 0b010),
            1 => Quad::new(0b110, 0b111, 0b101, 0b100),
            2 => Quad::new(0b100, 0b101, 0b001, 0b000),
            3 => Quad::new(0b011, 0b111, 0b110, 0b010),
            4 => Quad::new(0b000, 0b010, 0b110, 0b100),
            5 => Quad::new(0b101, 0b111, 0b011, 0b001),
            idx => fail!("{} face is higher then 6", idx)
        }
    }

    fn face(&self, idx: uint) -> Quad<(f32, f32, f32)> {
        self.face_indexed(idx).map_vertex(|i| self.vert(i))
    }
}

impl Iterator<Quad<(f32, f32, f32)>> for Cube {
    fn next(&mut self) -> Option<Quad<(f32, f32, f32)>> {
        self.range.next().map(|idx| self.face(idx))
    }
}

impl SharedVertex<(f32, f32, f32)> for Cube {
    fn shared_vertex(&self, idx: uint) -> (f32, f32, f32) {
        self.vert(idx)
    }

    fn shared_vertex_count(&self) -> uint { 8 }
}

impl IndexedPolygon<Quad<uint>> for Cube {
    fn indexed_polygon(&self, idx: uint) -> Quad<uint> {
        self.face_indexed(idx)
    }

    fn indexed_polygon_count(&self) -> uint { 6 }
}<commit_msg>Changed fail macro to panic (compatibility with rust-lang/rust#17894)<commit_after>//   Copyright Colin Sherratt 2014
//
//   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.

use core::iter::Range;
use super::{MapVertex, Quad};
use super::generators::{SharedVertex, IndexedPolygon};

/// A perfect cube, centered at (0, 0, 0) with each face starting at 1/-1 away from the origin
pub struct Cube {
    range: Range<uint>
}

impl Cube {
    /// create a new cube generator
    pub fn new() -> Cube {
        Cube { range: range(0, 6) }
    }

    fn vert(&self, idx: uint) -> (f32, f32, f32) {
        let x = if idx & 4 == 4 { 1.} else { -1. };
        let y = if idx & 2 == 2 { 1.} else { -1. };
        let z = if idx & 1 == 1 { 1.} else { -1. };
        (x, y, z)
    }

    fn face_indexed(&self, idx: uint) -> Quad<uint> {
        match idx {
            0 => Quad::new(0b000, 0b001, 0b011, 0b010),
            1 => Quad::new(0b110, 0b111, 0b101, 0b100),
            2 => Quad::new(0b100, 0b101, 0b001, 0b000),
            3 => Quad::new(0b011, 0b111, 0b110, 0b010),
            4 => Quad::new(0b000, 0b010, 0b110, 0b100),
            5 => Quad::new(0b101, 0b111, 0b011, 0b001),
            idx => panic!("{} face is higher then 6", idx)
        }
    }

    fn face(&self, idx: uint) -> Quad<(f32, f32, f32)> {
        self.face_indexed(idx).map_vertex(|i| self.vert(i))
    }
}

impl Iterator<Quad<(f32, f32, f32)>> for Cube {
    fn next(&mut self) -> Option<Quad<(f32, f32, f32)>> {
        self.range.next().map(|idx| self.face(idx))
    }
}

impl SharedVertex<(f32, f32, f32)> for Cube {
    fn shared_vertex(&self, idx: uint) -> (f32, f32, f32) {
        self.vert(idx)
    }

    fn shared_vertex_count(&self) -> uint { 8 }
}

impl IndexedPolygon<Quad<uint>> for Cube {
    fn indexed_polygon(&self, idx: uint) -> Quad<uint> {
        self.face_indexed(idx)
    }

    fn indexed_polygon_count(&self) -> uint { 6 }
}<|endoftext|>


<commit_before>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const COMMAND_READY: usize = 1 << 0;
const DATA_READY: usize = 1 << 1;
const ERROR: usize = 1 << 7;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(0)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!COMMAND_READY, Ordering::SeqCst) & COMMAND_READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(COMMAND_READY | ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            let byte;
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                byte = buffer.bytes[buffer.i as usize];
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        }
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                buffer.bytes[buffer.i as usize] = value;
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 17],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let mut file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(err) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 17] = [0; 17];
        for i in 0..17 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = (dpb[2] as u16);
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            while !(*parameters).do_command {
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            if die.load(Ordering::Acquire) { break; }
            let status = self.status.fetch_and(!DATA_READY, Ordering::SeqCst);
            if (status & ERROR) != 0 && parameters.command != RESET {
                self.status.fetch_or(DATA_READY, Ordering::SeqCst);
                continue;
            };
            let mut buffer = self.buffer.lock().unwrap();
            match parameters.command {
                NOP => (),
                SEL_DSK => {
                    if buffer.bytes[0] < MAX_DISK {
                        parameters.disk = buffer.bytes[0];
                    } else {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                    }
                },
                SEL_TRK => {
                    let track = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            if track < disk.tracks {
                                parameters.track = track;
                            } else {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    }
                },
                SEL_SEC => {
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            let sector = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                            if sector < disk.spt {
                                parameters.sector = sector;
                            } else {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }
                },
                READ => {
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            let off = ((parameters.track as usize * disk.spt as usize)
                                       + parameters.sector as usize) * SECTOR_SIZE as usize;
                            let bytes = unsafe { disk.view.as_slice() };
                            for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                *byte = bytes[off + i];
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }
                },
                WRITE => {
                    match disks[parameters.disk as usize] {
                        Some(ref mut disk) => {
                            let off = ((parameters.track as usize * disk.spt as usize)
                                       + parameters.sector as usize) * SECTOR_SIZE as usize;
                            let bytes = unsafe { disk.view.as_mut_slice() };
                            for (i, byte) in buffer.bytes.iter().enumerate() {
                                bytes[i] = *byte;
                            };
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }

                }
                _ => (),
            }
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
        }
    }
}
<commit_msg>Oops, can't count.<commit_after>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const COMMAND_READY: usize = 1 << 0;
const DATA_READY: usize = 1 << 1;
const ERROR: usize = 1 << 7;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(0)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!COMMAND_READY, Ordering::SeqCst) & COMMAND_READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(COMMAND_READY | ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            let byte;
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                byte = buffer.bytes[buffer.i as usize];
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        }
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                buffer.bytes[buffer.i as usize] = value;
            }
            self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 16],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let mut file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(err) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 16] = [0; 16];
        for i in 0..17 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = (dpb[2] as u16);
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            while !(*parameters).do_command {
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            if die.load(Ordering::Acquire) { break; }
            let status = self.status.fetch_and(!DATA_READY, Ordering::SeqCst);
            if (status & ERROR) != 0 && parameters.command != RESET {
                self.status.fetch_or(DATA_READY, Ordering::SeqCst);
                continue;
            };
            let mut buffer = self.buffer.lock().unwrap();
            match parameters.command {
                NOP => (),
                SEL_DSK => {
                    if buffer.bytes[0] < MAX_DISK {
                        parameters.disk = buffer.bytes[0];
                    } else {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                    }
                },
                SEL_TRK => {
                    let track = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            if track < disk.tracks {
                                parameters.track = track;
                            } else {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    }
                },
                SEL_SEC => {
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            let sector = buffer.bytes[0] as u16 & ((buffer.bytes[1] as u16) << 8);
                            if sector < disk.spt {
                                parameters.sector = sector;
                            } else {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }
                },
                READ => {
                    match disks[parameters.disk as usize] {
                        Some(ref disk) => {
                            let off = ((parameters.track as usize * disk.spt as usize)
                                       + parameters.sector as usize) * SECTOR_SIZE as usize;
                            let bytes = unsafe { disk.view.as_slice() };
                            for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                *byte = bytes[off + i];
                            }
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }
                },
                WRITE => {
                    match disks[parameters.disk as usize] {
                        Some(ref mut disk) => {
                            let off = ((parameters.track as usize * disk.spt as usize)
                                       + parameters.sector as usize) * SECTOR_SIZE as usize;
                            let bytes = unsafe { disk.view.as_mut_slice() };
                            for (i, byte) in buffer.bytes.iter().enumerate() {
                                bytes[i] = *byte;
                            };
                        },
                        None => {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        },
                    }

                }
                _ => (),
            }
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::ffi::CStr;
use std::rc::Rc;
use { ffi, FtResult, GlyphSlot, Matrix, Vector };

#[repr(u32)]
#[derive(Copy, Clone)]
pub enum KerningMode {
    KerningDefault  = ffi::FT_KERNING_DEFAULT,
    KerningUnfitted = ffi::FT_KERNING_UNFITTED,
    KerningUnscaled = ffi::FT_KERNING_UNSCALED
}

bitflags! {
    pub struct LoadFlag: i32 {
        const DEFAULT                    = ::ffi::FT_LOAD_DEFAULT;
        const NO_SCALE                   = ::ffi::FT_LOAD_NO_SCALE;
        const NO_HINTING                 = ::ffi::FT_LOAD_NO_HINTING;
        const RENDER                     = ::ffi::FT_LOAD_RENDER;
        const NO_BITMAP                  = ::ffi::FT_LOAD_NO_BITMAP;
        const VERTICAL_LAYOUT            = ::ffi::FT_LOAD_VERTICAL_LAYOUT;
        const FORCE_AUTOHINT             = ::ffi::FT_LOAD_FORCE_AUTOHINT;
        const CROP_BITMAP                = ::ffi::FT_LOAD_CROP_BITMAP;
        const PEDANTIC                   = ::ffi::FT_LOAD_PEDANTIC;
        const IGNORE_GLOBAL_ADVANCE_WITH = ::ffi::FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
        const NO_RECURSE                 = ::ffi::FT_LOAD_NO_RECURSE;
        const IGNORE_TRANSFORM           = ::ffi::FT_LOAD_IGNORE_TRANSFORM;
        const MONOCHROME                 = ::ffi::FT_LOAD_MONOCHROME;
        const LINEAR_DESIGN              = ::ffi::FT_LOAD_LINEAR_DESIGN;
        const NO_AUTOHINT                = ::ffi::FT_LOAD_NO_AUTOHINT;
        const TARGET_NORMAL              = ::ffi::FT_LOAD_TARGET_NORMAL;
        const TARGET_LIGHT               = ::ffi::FT_LOAD_TARGET_LIGHT;
        const TARGET_MONO                = ::ffi::FT_LOAD_TARGET_MONO;
        const TARGET_LCD                 = ::ffi::FT_LOAD_TARGET_LCD;
        const TARGET_LCD_V               = ::ffi::FT_LOAD_TARGET_LCD_V;
        const COLOR                      = ::ffi::FT_LOAD_COLOR;
    }
}

#[derive(Eq, PartialEq, Hash)]
pub struct Face {
    library_raw: ffi::FT_Library,
    raw: ffi::FT_Face,
    glyph: GlyphSlot,
    bytes: Option<Rc<Vec<u8>>>
}

impl Clone for Face {
    fn clone(&self) -> Self {
        let err = unsafe {
            ffi::FT_Reference_Library(self.library_raw)
        };
        if err != ffi::FT_Err_Ok {
            panic!("Failed to reference library");
        }
        let err = unsafe {
            ffi::FT_Reference_Face(self.raw)
        };
        if err != ffi::FT_Err_Ok {
            panic!("Failed to reference face");
        }
        Face {
            library_raw: self.library_raw,
            raw: self.raw,
            glyph: self.glyph,
            bytes: self.bytes.clone()
        }
    }
}

impl Face {
    pub unsafe fn from_raw(library_raw: ffi::FT_Library, raw: ffi::FT_Face, bytes: Option<Rc<Vec<u8>>>) -> Self {
        ffi::FT_Reference_Library(library_raw);
        Face {
            library_raw: library_raw,
            raw: raw,
            glyph: GlyphSlot::from_raw(library_raw, (*raw).glyph),
            bytes: bytes,
        }
    }

    pub fn attach_file(&self, filepathname: &str) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Attach_File(self.raw, filepathname.as_ptr() as *const _)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn reference(&self) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Reference_Face(self.raw)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn set_char_size(&self, char_width: isize, char_height: isize, horz_resolution: u32,
                         vert_resolution: u32) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Set_Char_Size(self.raw, char_width as ffi::FT_F26Dot6,
                                  char_height as ffi::FT_F26Dot6, horz_resolution,
                                  vert_resolution)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn set_pixel_sizes(&self, pixel_width: u32, pixel_height: u32) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Set_Pixel_Sizes(self.raw, pixel_width, pixel_height)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn load_glyph(&self, glyph_index: u32, load_flags: LoadFlag) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Load_Glyph(self.raw, glyph_index, load_flags.bits)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn load_char(&self, char_code: usize, load_flags: LoadFlag) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Load_Char(self.raw, char_code as ffi::FT_ULong, load_flags.bits)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn set_transform(&self, matrix: &mut Matrix, delta: &mut Vector) {
        unsafe {
            ffi::FT_Set_Transform(self.raw, matrix, delta);
        }
    }

    pub fn get_char_index(&self, charcode: usize) -> u32 {
        unsafe {
            ffi::FT_Get_Char_Index(self.raw, charcode as ffi::FT_ULong)
        }
    }

    pub fn get_kerning(&self, left_char_index: u32, right_char_index: u32, kern_mode: KerningMode)
        -> FtResult<Vector> {
        let mut vec = Vector { x: 0, y: 0 };

        let err = unsafe {
            ffi::FT_Get_Kerning(self.raw, left_char_index, right_char_index,
                                kern_mode as u32, &mut vec)
        };
        if err == ffi::FT_Err_Ok {
            Ok(vec)
        } else {
            Err(err.into())
        }
    }

    // According to FreeType doc, each time you load a new glyph image,
    // the previous one is erased from the glyph slot.
    #[inline(always)]
    pub fn glyph(&self) -> &GlyphSlot {
        &self.glyph
    }

    #[inline(always)]
    pub fn has_horizontal(&self) -> bool {
        ffi::FT_HAS_HORIZONTAL(self.raw)
    }

    #[inline(always)]
    pub fn has_vertical(&self) -> bool {
        ffi::FT_HAS_VERTICAL(self.raw)
    }

    #[inline(always)]
    pub fn has_kerning(&self) -> bool {
        ffi::FT_HAS_KERNING(self.raw)
    }

    #[inline(always)]
    pub fn is_scalable(&self) -> bool {
        ffi::FT_IS_SCALABLE(self.raw)
    }

    #[inline(always)]
    pub fn is_sfnt(&self) -> bool {
        ffi::FT_IS_SFNT(self.raw)
    }

    #[inline(always)]
    pub fn is_fixed_width(&self) -> bool {
        ffi::FT_IS_FIXED_WIDTH(self.raw)
    }

    #[inline(always)]
    pub fn has_fixed_sizes(&self) -> bool {
        ffi::FT_HAS_FIXED_SIZES(self.raw)
    }

    #[inline(always)]
    pub fn has_glyph_names(&self) -> bool {
        ffi::FT_HAS_GLYPH_NAMES(self.raw)
    }

    #[inline(always)]
    pub fn is_cid_keyed(&self) -> bool {
        ffi::FT_IS_CID_KEYED(self.raw)
    }

    #[inline(always)]
    pub fn is_tricky(&self) -> bool {
        ffi::FT_IS_TRICKY(self.raw)
    }

    #[inline(always)]
    pub fn has_color(&self) -> bool {
        ffi::FT_HAS_COLOR(self.raw)
    }

    #[inline(always)]
    pub fn raw(&self) -> &ffi::FT_FaceRec {
        unsafe {
            &*self.raw
        }
    }

    #[inline(always)]
    pub fn raw_mut(&mut self) -> &mut ffi::FT_FaceRec {
        unsafe {
            &mut *self.raw
        }
    }

    #[inline(always)]
    pub fn ascender(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).ascender
        }
    }

    #[inline(always)]
    pub fn descender(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).descender
        }
    }

    #[inline(always)]
    pub fn em_size(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).units_per_EM as i16
        }
    }

    #[inline(always)]
    pub fn height(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).height
        }
    }

    #[inline(always)]
    pub fn max_advance_width(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).max_advance_width
        }
    }

    #[inline(always)]
    pub fn max_advance_height(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).max_advance_height
        }
    }

    #[inline(always)]
    pub fn underline_position(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).underline_position
        }
    }

    #[inline(always)]
    pub fn underline_thickness(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).underline_thickness
        }
    }

    pub fn family_name(&self) -> Option<String> {
        let family_name = unsafe { (*self.raw).family_name };

        if family_name.is_null() {
            None
        } else {
            let family_name = unsafe {
                CStr::from_ptr(family_name as *const _).to_bytes().to_vec()
            };
            String::from_utf8(family_name).ok()
        }
    }

    pub fn style_name(&self) -> Option<String> {
        let style_name = unsafe { (*self.raw).style_name };

        if style_name.is_null() {
            None
        } else {
            let style_name = unsafe {
                CStr::from_ptr(style_name as *const _).to_bytes().to_vec()
            };
            String::from_utf8(style_name).ok()
        }
    }

    pub fn size_metrics(&self) -> Option<ffi::FT_Size_Metrics> {
        if self.raw.is_null() {
            None
        } else {
            let size = unsafe { (*self.raw).size };
            if size.is_null() {
                None
            } else {
                Some(unsafe { (*size).metrics })
            }
        }
    }

    pub fn postscript_name(&self) -> Option<String> {
        let face_name = unsafe { ffi::FT_Get_Postscript_Name(self.raw) };
        if face_name.is_null() {
            None
        } else {
            let face_name = unsafe {
                CStr::from_ptr(face_name as *const _).to_bytes().to_vec()
            };
            String::from_utf8(face_name).ok()
        }
    }
}

impl fmt::Debug for Face {
    fn fmt(&self, form: &mut fmt::Formatter) -> fmt::Result {
        let name = self.style_name().unwrap_or("[unknown name]".to_owned());
        try!(form.write_str("Font Face: "));
        form.write_str(&name[..])
    }
}

impl Drop for Face {
    fn drop(&mut self) {
        let err = unsafe {
            ffi::FT_Done_Face(self.raw)
        };
        if err != ffi::FT_Err_Ok {
            panic!("Failed to drop face");
        }
        let err = unsafe {
            ffi::FT_Done_Library(self.library_raw)
        };
        if err != ffi::FT_Err_Ok {
            panic!("Failed to drop library")
        }
        self.bytes = None;
    }
}
<commit_msg>Add Face::style_flags()<commit_after>use std::fmt;
use std::ffi::CStr;
use std::rc::Rc;
use { ffi, FtResult, GlyphSlot, Matrix, Vector };

#[repr(u32)]
#[derive(Copy, Clone)]
pub enum KerningMode {
    KerningDefault  = ffi::FT_KERNING_DEFAULT,
    KerningUnfitted = ffi::FT_KERNING_UNFITTED,
    KerningUnscaled = ffi::FT_KERNING_UNSCALED
}

bitflags! {
    pub struct LoadFlag: i32 {
        const DEFAULT                    = ::ffi::FT_LOAD_DEFAULT;
        const NO_SCALE                   = ::ffi::FT_LOAD_NO_SCALE;
        const NO_HINTING                 = ::ffi::FT_LOAD_NO_HINTING;
        const RENDER                     = ::ffi::FT_LOAD_RENDER;
        const NO_BITMAP                  = ::ffi::FT_LOAD_NO_BITMAP;
        const VERTICAL_LAYOUT            = ::ffi::FT_LOAD_VERTICAL_LAYOUT;
        const FORCE_AUTOHINT             = ::ffi::FT_LOAD_FORCE_AUTOHINT;
        const CROP_BITMAP                = ::ffi::FT_LOAD_CROP_BITMAP;
        const PEDANTIC                   = ::ffi::FT_LOAD_PEDANTIC;
        const IGNORE_GLOBAL_ADVANCE_WITH = ::ffi::FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
        const NO_RECURSE                 = ::ffi::FT_LOAD_NO_RECURSE;
        const IGNORE_TRANSFORM           = ::ffi::FT_LOAD_IGNORE_TRANSFORM;
        const MONOCHROME                 = ::ffi::FT_LOAD_MONOCHROME;
        const LINEAR_DESIGN              = ::ffi::FT_LOAD_LINEAR_DESIGN;
        const NO_AUTOHINT                = ::ffi::FT_LOAD_NO_AUTOHINT;
        const TARGET_NORMAL              = ::ffi::FT_LOAD_TARGET_NORMAL;
        const TARGET_LIGHT               = ::ffi::FT_LOAD_TARGET_LIGHT;
        const TARGET_MONO                = ::ffi::FT_LOAD_TARGET_MONO;
        const TARGET_LCD                 = ::ffi::FT_LOAD_TARGET_LCD;
        const TARGET_LCD_V               = ::ffi::FT_LOAD_TARGET_LCD_V;
        const COLOR                      = ::ffi::FT_LOAD_COLOR;
    }
}

bitflags! {
    pub struct StyleFlag: ffi::FT_Long {
        const BOLD   = ::ffi::FT_STYLE_FLAG_BOLD;
        const ITALIC = ::ffi::FT_STYLE_FLAG_ITALIC;
    }
}

#[derive(Eq, PartialEq, Hash)]
pub struct Face {
    library_raw: ffi::FT_Library,
    raw: ffi::FT_Face,
    glyph: GlyphSlot,
    bytes: Option<Rc<Vec<u8>>>
}

impl Clone for Face {
    fn clone(&self) -> Self {
        let err = unsafe {
            ffi::FT_Reference_Library(self.library_raw)
        };
        if err != ffi::FT_Err_Ok {
            panic!("Failed to reference library");
        }
        let err = unsafe {
            ffi::FT_Reference_Face(self.raw)
        };
        if err != ffi::FT_Err_Ok {
            panic!("Failed to reference face");
        }
        Face {
            library_raw: self.library_raw,
            raw: self.raw,
            glyph: self.glyph,
            bytes: self.bytes.clone()
        }
    }
}

impl Face {
    pub unsafe fn from_raw(library_raw: ffi::FT_Library, raw: ffi::FT_Face, bytes: Option<Rc<Vec<u8>>>) -> Self {
        ffi::FT_Reference_Library(library_raw);
        Face {
            library_raw: library_raw,
            raw: raw,
            glyph: GlyphSlot::from_raw(library_raw, (*raw).glyph),
            bytes: bytes,
        }
    }

    pub fn attach_file(&self, filepathname: &str) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Attach_File(self.raw, filepathname.as_ptr() as *const _)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn reference(&self) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Reference_Face(self.raw)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn set_char_size(&self, char_width: isize, char_height: isize, horz_resolution: u32,
                         vert_resolution: u32) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Set_Char_Size(self.raw, char_width as ffi::FT_F26Dot6,
                                  char_height as ffi::FT_F26Dot6, horz_resolution,
                                  vert_resolution)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn set_pixel_sizes(&self, pixel_width: u32, pixel_height: u32) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Set_Pixel_Sizes(self.raw, pixel_width, pixel_height)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn load_glyph(&self, glyph_index: u32, load_flags: LoadFlag) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Load_Glyph(self.raw, glyph_index, load_flags.bits)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn load_char(&self, char_code: usize, load_flags: LoadFlag) -> FtResult<()> {
        let err = unsafe {
            ffi::FT_Load_Char(self.raw, char_code as ffi::FT_ULong, load_flags.bits)
        };
        if err == ffi::FT_Err_Ok {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    pub fn set_transform(&self, matrix: &mut Matrix, delta: &mut Vector) {
        unsafe {
            ffi::FT_Set_Transform(self.raw, matrix, delta);
        }
    }

    pub fn get_char_index(&self, charcode: usize) -> u32 {
        unsafe {
            ffi::FT_Get_Char_Index(self.raw, charcode as ffi::FT_ULong)
        }
    }

    pub fn get_kerning(&self, left_char_index: u32, right_char_index: u32, kern_mode: KerningMode)
        -> FtResult<Vector> {
        let mut vec = Vector { x: 0, y: 0 };

        let err = unsafe {
            ffi::FT_Get_Kerning(self.raw, left_char_index, right_char_index,
                                kern_mode as u32, &mut vec)
        };
        if err == ffi::FT_Err_Ok {
            Ok(vec)
        } else {
            Err(err.into())
        }
    }

    // According to FreeType doc, each time you load a new glyph image,
    // the previous one is erased from the glyph slot.
    #[inline(always)]
    pub fn glyph(&self) -> &GlyphSlot {
        &self.glyph
    }

    #[inline(always)]
    pub fn has_horizontal(&self) -> bool {
        ffi::FT_HAS_HORIZONTAL(self.raw)
    }

    #[inline(always)]
    pub fn has_vertical(&self) -> bool {
        ffi::FT_HAS_VERTICAL(self.raw)
    }

    #[inline(always)]
    pub fn has_kerning(&self) -> bool {
        ffi::FT_HAS_KERNING(self.raw)
    }

    #[inline(always)]
    pub fn is_scalable(&self) -> bool {
        ffi::FT_IS_SCALABLE(self.raw)
    }

    #[inline(always)]
    pub fn is_sfnt(&self) -> bool {
        ffi::FT_IS_SFNT(self.raw)
    }

    #[inline(always)]
    pub fn is_fixed_width(&self) -> bool {
        ffi::FT_IS_FIXED_WIDTH(self.raw)
    }

    #[inline(always)]
    pub fn has_fixed_sizes(&self) -> bool {
        ffi::FT_HAS_FIXED_SIZES(self.raw)
    }

    #[inline(always)]
    pub fn has_glyph_names(&self) -> bool {
        ffi::FT_HAS_GLYPH_NAMES(self.raw)
    }

    #[inline(always)]
    pub fn is_cid_keyed(&self) -> bool {
        ffi::FT_IS_CID_KEYED(self.raw)
    }

    #[inline(always)]
    pub fn is_tricky(&self) -> bool {
        ffi::FT_IS_TRICKY(self.raw)
    }

    #[inline(always)]
    pub fn has_color(&self) -> bool {
        ffi::FT_HAS_COLOR(self.raw)
    }

    #[inline(always)]
    pub fn raw(&self) -> &ffi::FT_FaceRec {
        unsafe {
            &*self.raw
        }
    }

    #[inline(always)]
    pub fn raw_mut(&mut self) -> &mut ffi::FT_FaceRec {
        unsafe {
            &mut *self.raw
        }
    }

    #[inline(always)]
    pub fn ascender(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).ascender
        }
    }

    #[inline(always)]
    pub fn descender(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).descender
        }
    }

    #[inline(always)]
    pub fn em_size(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).units_per_EM as i16
        }
    }

    #[inline(always)]
    pub fn height(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).height
        }
    }

    #[inline(always)]
    pub fn max_advance_width(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).max_advance_width
        }
    }

    #[inline(always)]
    pub fn max_advance_height(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).max_advance_height
        }
    }

    #[inline(always)]
    pub fn underline_position(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).underline_position
        }
    }

    #[inline(always)]
    pub fn underline_thickness(&self) -> ffi::FT_Short {
        unsafe {
            (*self.raw).underline_thickness
        }
    }

    pub fn family_name(&self) -> Option<String> {
        let family_name = unsafe { (*self.raw).family_name };

        if family_name.is_null() {
            None
        } else {
            let family_name = unsafe {
                CStr::from_ptr(family_name as *const _).to_bytes().to_vec()
            };
            String::from_utf8(family_name).ok()
        }
    }

    pub fn style_name(&self) -> Option<String> {
        let style_name = unsafe { (*self.raw).style_name };

        if style_name.is_null() {
            None
        } else {
            let style_name = unsafe {
                CStr::from_ptr(style_name as *const _).to_bytes().to_vec()
            };
            String::from_utf8(style_name).ok()
        }
    }

    pub fn style_flags(&self) -> StyleFlag {
        let style_flags = unsafe { (*self.raw).style_flags };
        StyleFlag::from_bits_truncate(style_flags)
    }

    pub fn size_metrics(&self) -> Option<ffi::FT_Size_Metrics> {
        if self.raw.is_null() {
            None
        } else {
            let size = unsafe { (*self.raw).size };
            if size.is_null() {
                None
            } else {
                Some(unsafe { (*size).metrics })
            }
        }
    }

    pub fn postscript_name(&self) -> Option<String> {
        let face_name = unsafe { ffi::FT_Get_Postscript_Name(self.raw) };
        if face_name.is_null() {
            None
        } else {
            let face_name = unsafe {
                CStr::from_ptr(face_name as *const _).to_bytes().to_vec()
            };
            String::from_utf8(face_name).ok()
        }
    }
}

impl fmt::Debug for Face {
    fn fmt(&self, form: &mut fmt::Formatter) -> fmt::Result {
        let name = self.style_name().unwrap_or("[unknown name]".to_owned());
        try!(form.write_str("Font Face: "));
        form.write_str(&name[..])
    }
}

impl Drop for Face {
    fn drop(&mut self) {
        let err = unsafe {
            ffi::FT_Done_Face(self.raw)
        };
        if err != ffi::FT_Err_Ok {
            panic!("Failed to drop face");
        }
        let err = unsafe {
            ffi::FT_Done_Library(self.library_raw)
        };
        if err != ffi::FT_Err_Ok {
            panic!("Failed to drop library")
        }
        self.bytes = None;
    }
}
<|endoftext|>


<commit_before>use ffi::types::{hid_t, hsize_t, hbool_t};
use ffi::h5i::H5I_INVALID_HID;
use ffi::h5p::{H5P_FILE_CREATE, H5P_FILE_ACCESS, H5Pcreate, H5Pset_userblock};
use ffi::h5f::{H5F_ACC_RDONLY, H5F_ACC_RDWR, H5F_ACC_EXCL, H5F_ACC_TRUNC,
               H5Fopen, H5Fcreate, H5Fclose, H5Fget_filesize, H5Fget_intent,
               H5Fget_access_plist, H5Fget_create_plist};
use ffi::drivers::{H5Pset_fapl_sec2, H5Pset_fapl_stdio, H5Pset_fapl_core};

use object::{Handle, Object};
use error::Result;
use plist::PropertyList;
use ffi::util::string_to_cstr;
use std::path::{Path, PathBuf};

use libc::{size_t, c_uint};

#[derive(Clone)]
pub struct File {
    handle: Handle,
}

impl Object for File {
    fn id(&self) -> hid_t {
        self.handle.id()
    }

    fn from_id(id: hid_t) -> File {
        File { handle: Handle::new(id) }
    }
}

impl File {
    pub fn size(&self) -> u64 {
        unsafe {
            let size: *mut hsize_t = &mut 0;
            h5lock_s!(H5Fget_filesize(self.id(), size));
            if *size > 0 { *size as u64 } else { 0 }
        }
    }

    pub fn is_read_only(&self) -> bool {
        unsafe {
            let mode: *mut c_uint = &mut 0;
            h5lock_s!(H5Fget_intent(self.id(), mode));
            *mode != H5F_ACC_RDWR
        }
    }

    pub fn open<P: AsRef<Path>, S: Into<String>>(filename: P, mode: S) -> Result<File> {
        FileBuilder::new(filename).mode(mode).open()
    }

    fn fapl(&self) -> PropertyList {
        PropertyList::from_id(h5call!(H5Fget_access_plist(self.id())).unwrap_or(H5I_INVALID_HID))
    }

    fn fcpl(&self) -> PropertyList {
        PropertyList::from_id(h5call!(H5Fget_create_plist(self.id())).unwrap_or(H5I_INVALID_HID))
    }
}

impl Drop for File {
    fn drop(&mut self) {
        if self.refcount() == 1 {
            h5lock!(H5Fclose(self.id()));
        }
    }
}

pub struct FileBuilder {
    filename: PathBuf,
    driver: String,
    mode: String,
    userblock: size_t,
    filebacked: bool,
    increment: size_t,
}

impl FileBuilder {
    pub fn new<P: AsRef<Path>>(filename: P) -> FileBuilder {
        FileBuilder {
            filename: filename.as_ref().to_path_buf(),
            driver: "sec2".to_string(),
            mode: "r".to_string(),
            userblock: 0,
            filebacked: false,
            increment: 64 * 1024 * 1024,
        }
    }

    pub fn driver<S: Into<String>>(&mut self, driver: S) -> &mut FileBuilder {
        self.driver = driver.into(); self
    }

    pub fn mode<S: Into<String>>(&mut self, mode: S) -> &mut FileBuilder {
        self.mode = mode.into(); self
    }

    pub fn userblock(&mut self, userblock: size_t) -> &mut FileBuilder {
        self.userblock = userblock; self
    }

    pub fn filebacked(&mut self, filebacked: bool) -> &mut FileBuilder {
        self.filebacked = filebacked; self
    }

    pub fn increment(&mut self, increment: size_t) -> &mut FileBuilder {
        self.increment = increment; self
    }

    fn make_fapl(&self) -> Result<PropertyList> {
        h5lock_s!({
            let fapl = PropertyList::from_id(h5try!(H5Pcreate(*H5P_FILE_ACCESS)));
            match self.driver.as_ref() {
                "sec2"  => h5try!(H5Pset_fapl_sec2(fapl.id())),
                "stdio" => h5try!(H5Pset_fapl_stdio(fapl.id())),
                "core"  => h5try!(H5Pset_fapl_core(fapl.id(), self.increment,
                                                   self.filebacked as hbool_t)),
                _       => fail!(format!("Invalid file driver: {}", self.driver)),
            };
            Ok(fapl)
        })
    }

    fn open_file(&self, write: bool) -> Result<File> {
        ensure!(self.userblock == 0, "Cannot specify userblock when opening a file");
        h5lock_s!({
            let fapl = try!(self.make_fapl());
            let flags = if write { H5F_ACC_RDWR } else { H5F_ACC_RDONLY };
            match self.filename.to_str() {
                Some(filename) => {
                    let c_filename = string_to_cstr(filename);
                    let file = File::from_id(h5try!(H5Fopen(c_filename, flags, fapl.id())));
                    ensure!(file.is_valid(), "Invalid id for opened file");
                    Ok(file)
                },
                None          => fail!("Invalid UTF-8 in file name: {:?}", self.filename)
            }
        })
    }

    fn create_file(&self, exclusive: bool) -> Result<File> {
        h5lock_s!({
            let fcpl = PropertyList::from_id(h5try!(H5Pcreate(*H5P_FILE_CREATE)));
            h5try!(H5Pset_userblock(fcpl.id(), self.userblock));
            let fapl = try!(self.make_fapl());
            let flags = if exclusive { H5F_ACC_EXCL } else { H5F_ACC_TRUNC };
            match self.filename.to_str() {
                Some(filename) => {
                    let c_filename = string_to_cstr(filename);
                    let file = File::from_id(h5try!(H5Fcreate(c_filename, flags,
                                                              fcpl.id(), fapl.id())));
                    ensure!(file.is_valid(), "Invalid id for created file");
                    Ok(file)
                },
                None          => fail!("Invalid UTF-8 in file name: {:?}", self.filename)
            }
        })
    }

    pub fn open(&self) -> Result<File> {
        match self.mode.as_ref() {
            "r"        => self.open_file(false),
            "r+"       => self.open_file(true),
            "w"        => self.create_file(false),
            "w-" | "x" => self.create_file(true),
            "a"        => match self.open_file(true) {
                            Ok(file) => Ok(file),
                            _        => self.create_file(true),
                          },
            _          => fail!("Invalid file access mode, expected r|r+|w|w-|x|a"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::File;
    use std::path::PathBuf;
    use tempdir::TempDir;

    fn with_tmpdir<F: Fn(PathBuf)>(f: F) {
        let dir = TempDir::new_in(".", "tmp").unwrap();
        let path = dir.path().to_path_buf();
        f(path);
    }

    #[test]
    pub fn test_invalid_mode() {
        with_tmpdir(|dir| {
            assert_err!(File::open(&dir, "foo"), "Invalid file access mode");
        })
    }

    #[test]
    pub fn test_is_read_only() {
        with_tmpdir(|dir| {
            let path = dir.join("foo.h5");
            assert!(!File::open(&path, "w").unwrap().is_read_only());
            assert!(File::open(&path, "r").unwrap().is_read_only());
            assert!(!File::open(&path, "r+").unwrap().is_read_only());
            assert!(!File::open(&path, "a").unwrap().is_read_only());
        });
        with_tmpdir(|dir| {
            assert!(!File::open(dir.join("foo.h"), "a").unwrap().is_read_only());
        });
        with_tmpdir(|dir| {
            assert!(!File::open(dir.join("foo.h"), "x").unwrap().is_read_only());
        });
    }

    #[test]
    pub fn test_invalid_filename() {
        with_tmpdir(|dir| {
            assert_err!(File::open(&dir, "r"), "unable to open file");
            assert_err!(File::open(&dir, "r+"), "unable to open file");
            assert_err!(File::open(&dir, "x"), "unable to create file");
            assert_err!(File::open(&dir, "w-"), "unable to create file");
            assert_err!(File::open(&dir, "w"), "unable to create file");
            assert_err!(File::open(&dir, "a"), "unable to create file");
        });
    }
}
<commit_msg>A few docstrings for future reference<commit_after>use ffi::types::{hid_t, hsize_t, hbool_t};
use ffi::h5i::H5I_INVALID_HID;
use ffi::h5p::{H5P_FILE_CREATE, H5P_FILE_ACCESS, H5Pcreate, H5Pset_userblock};
use ffi::h5f::{H5F_ACC_RDONLY, H5F_ACC_RDWR, H5F_ACC_EXCL, H5F_ACC_TRUNC,
               H5Fopen, H5Fcreate, H5Fclose, H5Fget_filesize, H5Fget_intent,
               H5Fget_access_plist, H5Fget_create_plist};
use ffi::drivers::{H5Pset_fapl_sec2, H5Pset_fapl_stdio, H5Pset_fapl_core};

use object::{Handle, Object};
use error::Result;
use plist::PropertyList;
use ffi::util::string_to_cstr;
use std::path::{Path, PathBuf};

use libc::{size_t, c_uint};

#[derive(Clone)]
pub struct File {
    handle: Handle,
}

impl Object for File {
    fn id(&self) -> hid_t {
        self.handle.id()
    }

    fn from_id(id: hid_t) -> File {
        File { handle: Handle::new(id) }
    }
}

impl File {
    /// Create a new file object.
    ///
    /// | `mode`    | File access mode
    /// |-----------|-----------------
    /// | `r`       | Read-only, file must exist
    /// | `r+`      | Read/write, file must exist
    /// | `w`       | Create file, truncate if exists
    /// | `w-`, `x` | Create file, fail if exists
    /// | `a`       | Read/write if exists, create otherwise
    pub fn open<P: AsRef<Path>, S: Into<String>>(filename: P, mode: S) -> Result<File> {
        FileBuilder::new(filename).mode(mode).open()
    }

    pub fn size(&self) -> u64 {
        unsafe {
            let size: *mut hsize_t = &mut 0;
            h5lock_s!(H5Fget_filesize(self.id(), size));
            if *size > 0 { *size as u64 } else { 0 }
        }
    }

    /// Returns true if the file was opened in a read-only mode.
    pub fn is_read_only(&self) -> bool {
        unsafe {
            let mode: *mut c_uint = &mut 0;
            h5lock_s!(H5Fget_intent(self.id(), mode));
            *mode != H5F_ACC_RDWR
        }
    }

    fn fapl(&self) -> PropertyList {
        PropertyList::from_id(h5call!(H5Fget_access_plist(self.id())).unwrap_or(H5I_INVALID_HID))
    }

    fn fcpl(&self) -> PropertyList {
        PropertyList::from_id(h5call!(H5Fget_create_plist(self.id())).unwrap_or(H5I_INVALID_HID))
    }
}

impl Drop for File {
    fn drop(&mut self) {
        if self.refcount() == 1 {
            h5lock!(H5Fclose(self.id()));
        }
    }
}

pub struct FileBuilder {
    filename: PathBuf,
    driver: String,
    mode: String,
    userblock: size_t,
    filebacked: bool,
    increment: size_t,
}

impl FileBuilder {
    pub fn new<P: AsRef<Path>>(filename: P) -> FileBuilder {
        FileBuilder {
            filename: filename.as_ref().to_path_buf(),
            driver: "sec2".to_string(),
            mode: "r".to_string(),
            userblock: 0,
            filebacked: false,
            increment: 64 * 1024 * 1024,
        }
    }

    pub fn driver<S: Into<String>>(&mut self, driver: S) -> &mut FileBuilder {
        self.driver = driver.into(); self
    }

    pub fn mode<S: Into<String>>(&mut self, mode: S) -> &mut FileBuilder {
        self.mode = mode.into(); self
    }

    pub fn userblock(&mut self, userblock: size_t) -> &mut FileBuilder {
        self.userblock = userblock; self
    }

    pub fn filebacked(&mut self, filebacked: bool) -> &mut FileBuilder {
        self.filebacked = filebacked; self
    }

    pub fn increment(&mut self, increment: size_t) -> &mut FileBuilder {
        self.increment = increment; self
    }

    fn make_fapl(&self) -> Result<PropertyList> {
        h5lock_s!({
            let fapl = PropertyList::from_id(h5try!(H5Pcreate(*H5P_FILE_ACCESS)));
            match self.driver.as_ref() {
                "sec2"  => h5try!(H5Pset_fapl_sec2(fapl.id())),
                "stdio" => h5try!(H5Pset_fapl_stdio(fapl.id())),
                "core"  => h5try!(H5Pset_fapl_core(fapl.id(), self.increment,
                                                   self.filebacked as hbool_t)),
                _       => fail!(format!("Invalid file driver: {}", self.driver)),
            };
            Ok(fapl)
        })
    }

    fn open_file(&self, write: bool) -> Result<File> {
        ensure!(self.userblock == 0, "Cannot specify userblock when opening a file");
        h5lock_s!({
            let fapl = try!(self.make_fapl());
            let flags = if write { H5F_ACC_RDWR } else { H5F_ACC_RDONLY };
            match self.filename.to_str() {
                Some(filename) => {
                    let c_filename = string_to_cstr(filename);
                    let file = File::from_id(h5try!(H5Fopen(c_filename, flags, fapl.id())));
                    ensure!(file.is_valid(), "Invalid id for opened file");
                    Ok(file)
                },
                None          => fail!("Invalid UTF-8 in file name: {:?}", self.filename)
            }
        })
    }

    fn create_file(&self, exclusive: bool) -> Result<File> {
        h5lock_s!({
            let fcpl = PropertyList::from_id(h5try!(H5Pcreate(*H5P_FILE_CREATE)));
            h5try!(H5Pset_userblock(fcpl.id(), self.userblock));
            let fapl = try!(self.make_fapl());
            let flags = if exclusive { H5F_ACC_EXCL } else { H5F_ACC_TRUNC };
            match self.filename.to_str() {
                Some(filename) => {
                    let c_filename = string_to_cstr(filename);
                    let file = File::from_id(h5try!(H5Fcreate(c_filename, flags,
                                                              fcpl.id(), fapl.id())));
                    ensure!(file.is_valid(), "Invalid id for created file");
                    Ok(file)
                },
                None          => fail!("Invalid UTF-8 in file name: {:?}", self.filename)
            }
        })
    }

    pub fn open(&self) -> Result<File> {
        match self.mode.as_ref() {
            "r"        => self.open_file(false),
            "r+"       => self.open_file(true),
            "w"        => self.create_file(false),
            "w-" | "x" => self.create_file(true),
            "a"        => match self.open_file(true) {
                            Ok(file) => Ok(file),
                            _        => self.create_file(true),
                          },
            _          => fail!("Invalid file access mode, expected r|r+|w|w-|x|a"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::File;
    use std::path::PathBuf;
    use tempdir::TempDir;

    fn with_tmpdir<F: Fn(PathBuf)>(f: F) {
        let dir = TempDir::new_in(".", "tmp").unwrap();
        let path = dir.path().to_path_buf();
        f(path);
    }

    #[test]
    pub fn test_invalid_mode() {
        with_tmpdir(|dir| {
            assert_err!(File::open(&dir, "foo"), "Invalid file access mode");
        })
    }

    #[test]
    pub fn test_is_read_only() {
        with_tmpdir(|dir| {
            let path = dir.join("foo.h5");
            assert!(!File::open(&path, "w").unwrap().is_read_only());
            assert!(File::open(&path, "r").unwrap().is_read_only());
            assert!(!File::open(&path, "r+").unwrap().is_read_only());
            assert!(!File::open(&path, "a").unwrap().is_read_only());
        });
        with_tmpdir(|dir| {
            assert!(!File::open(dir.join("foo.h"), "a").unwrap().is_read_only());
        });
        with_tmpdir(|dir| {
            assert!(!File::open(dir.join("foo.h"), "x").unwrap().is_read_only());
        });
    }

    #[test]
    pub fn test_invalid_filename() {
        with_tmpdir(|dir| {
            assert_err!(File::open(&dir, "r"), "unable to open file");
            assert_err!(File::open(&dir, "r+"), "unable to open file");
            assert_err!(File::open(&dir, "x"), "unable to create file");
            assert_err!(File::open(&dir, "w-"), "unable to create file");
            assert_err!(File::open(&dir, "w"), "unable to create file");
            assert_err!(File::open(&dir, "a"), "unable to create file");
        });
    }
}
<|endoftext|>