<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 {
    pub fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(DATA_READY)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
    pub fn status_port(&self) -> StatusPort {
        StatusPort::new(self.clone())
    }
    pub fn data_port(&self) -> DataPort {
        DataPort::new(self.clone())
    }
}

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 {
        let byte = 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];
            }
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        };
        self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        byte
    }
    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.bytes[buffer.i as usize] = value;
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
            }
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
        self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
    }
}

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 {
            if die.load(Ordering::Acquire) { break; }
            parameters.do_command = false;
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
            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();
                buffer.i = 0;
                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);
                            },
                        }

                    }
                    RESET => {
                        parameters.disk = 0;
                        parameters.track = 0;
                        parameters.sector = 0;
                        parameters.command = NOP;
                        self.status.fetch_and(!ERROR, Ordering::SeqCst);
                    },
                    OPEN => {
                        match str::from_utf8(buffer.bytes.split(|a| *a == 0).next().unwrap()) {
                            Ok(file_name) => {
                                match Disk::open(&file_name, Protection::ReadWrite) {
                                    Ok(disk) => {
                                        disks[parameters.disk as usize] = Some(disk);
                                    },
                                    Err(err) => {
                                        let mut stderr = io::stderr();
                                        let _ = writeln!(stderr, "disk: Failed to open file: {}", file_name);
                                        let _ = writeln!(stderr, "Error:\n\t{}", err);
                                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                                    },
                                }
                            },
                            Err(err) => {
                                let _ = write!(io::stderr(), "disk: Bad UTF-8 in file name.\nError:\n\t{}\n", err);
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    CLOSE => {
                        disks[parameters.disk as usize] = None;
                    },
                    DPB => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                for (a, b) in disk.dpb.iter().zip(buffer.bytes.iter_mut()) {
                                    *b = *a;
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    _ => {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                        let _ = write!(io::stderr(), "disk: System sent bad command: {:02X}\n", parameters.command);
                    },
                }
            }
            self.status.fetch_or(DATA_READY, Ordering::SeqCst);
        }
    }
}
<commit_msg>Fix all writes going to track 0 sector 0<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 {
    pub fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(DATA_READY)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
    pub fn status_port(&self) -> StatusPort {
        StatusPort::new(self.clone())
    }
    pub fn data_port(&self) -> DataPort {
        DataPort::new(self.clone())
    }
}

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 {
        let byte = 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];
            }
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        };
        self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        byte
    }
    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.bytes[buffer.i as usize] = value;
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
            }
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
        self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
    }
}

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 {
            if die.load(Ordering::Acquire) { break; }
            parameters.do_command = false;
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
            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();
                buffer.i = 0;
                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[off + i] = *byte;
                                };
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }

                    }
                    RESET => {
                        parameters.disk = 0;
                        parameters.track = 0;
                        parameters.sector = 0;
                        parameters.command = NOP;
                        self.status.fetch_and(!ERROR, Ordering::SeqCst);
                    },
                    OPEN => {
                        match str::from_utf8(buffer.bytes.split(|a| *a == 0).next().unwrap()) {
                            Ok(file_name) => {
                                match Disk::open(&file_name, Protection::ReadWrite) {
                                    Ok(disk) => {
                                        disks[parameters.disk as usize] = Some(disk);
                                    },
                                    Err(err) => {
                                        let mut stderr = io::stderr();
                                        let _ = writeln!(stderr, "disk: Failed to open file: {}", file_name);
                                        let _ = writeln!(stderr, "Error:\n\t{}", err);
                                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                                    },
                                }
                            },
                            Err(err) => {
                                let _ = write!(io::stderr(), "disk: Bad UTF-8 in file name.\nError:\n\t{}\n", err);
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    CLOSE => {
                        disks[parameters.disk as usize] = None;
                    },
                    DPB => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                for (a, b) in disk.dpb.iter().zip(buffer.bytes.iter_mut()) {
                                    *b = *a;
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    _ => {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                        let _ = write!(io::stderr(), "disk: System sent bad command: {:02X}\n", parameters.command);
                    },
                }
            }
            self.status.fetch_or(DATA_READY, Ordering::SeqCst);
        }
    }
}
<|endoftext|>


<commit_before>use super::object::Object;

use super::ast::Program;
use super::ast::Statement;
use super::ast::Expression;

const TRUE: Object = Object::Boolean(true);
const FALSE: Object = Object::Boolean(false);
const NULL: Object = Object::Null;

pub fn eval(program: Program) -> Object {
    let mut result = Object::Null;

    for stmt in program.statements {
        result = match stmt {
            Statement::Expression { ref expression, .. } => eval_expr(expression),
            _ => NULL
        };
    }

    result
}

fn eval_expr(expr: &Expression) -> Object {
    match *expr {
        Expression::IntegerLiteral { value, .. } => Object::Integer(value),
        Expression::Boolean { value, .. } => if value { TRUE } else { FALSE },
        Expression::Prefix { ref operator, ref right, .. } => {
            let right = eval_expr(right);
            eval_prefix_expr(operator, right)
        },
        _ => NULL
    }
}

fn eval_prefix_expr(op: &str, right: Object) -> Object {
    match op {
        "!" => eval_bang_operator_expr(right),
        _ => NULL
    }
}

fn eval_bang_operator_expr(right: Object) -> Object {
    match right {
        TRUE => FALSE,
        FALSE => TRUE,
        NULL => TRUE,
        _ => FALSE
    }
}

#[cfg(test)]
use super::lexer::Lexer;
#[cfg(test)]
use super::parser::Parser;

#[cfg(test)]
fn test_eval(input: &str) -> Object {
    let mut l = Lexer::new(input.to_string());
    let mut p = Parser::new(&mut l);
    let program = p.parse_program();

    eval(program)
}

#[cfg(test)]
fn check_integer_object(obj: Object, expected: i64) {
    match obj {
        Object::Integer(val) => {
            assert!(val == expected,
                    "Object has wrong value. Got {}, want {}",
                    val,
                    expected);
        },
        _ => panic!("Object is not an Integer. Got {:?}", obj)
    }
}

#[cfg(test)]
fn check_boolean_object(obj: Object, expected: bool) {
    match obj {
        Object::Boolean(val) => {
            assert!(val == expected,
                    "Object has wrong value. Got {}, want {}",
                    val,
                    expected);
        },
        _ => panic!("Object is not an Boolean. Got {:?}", obj)
    }
}

#[test]
fn test_eval_integer_expression() {
    let tests = vec![
        ("5", 5),
        ("10", 10)
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_integer_object(evaluated, expected);
    }
}

#[test]
fn test_eval_boolean_expression() {
    let tests = vec![
        ("true", true),
        ("false", false)
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_boolean_object(evaluated, expected);
    }
}

#[test]
fn test_bang_operator() {
    let tests = vec![
        ("!true", false),
        ("!false", true),
        ("!5", false),
        ("!!true", true),
        ("!!false", false),
        ("!!5", true),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_boolean_object(evaluated, expected);
    }
}<commit_msg>Minus op evaluation.<commit_after>use super::object::Object;

use super::ast::Program;
use super::ast::Statement;
use super::ast::Expression;

const TRUE: Object = Object::Boolean(true);
const FALSE: Object = Object::Boolean(false);
const NULL: Object = Object::Null;

pub fn eval(program: Program) -> Object {
    let mut result = Object::Null;

    for stmt in program.statements {
        result = match stmt {
            Statement::Expression { ref expression, .. } => eval_expr(expression),
            _ => NULL
        };
    }

    result
}

fn eval_expr(expr: &Expression) -> Object {
    match *expr {
        Expression::IntegerLiteral { value, .. } => Object::Integer(value),
        Expression::Boolean { value, .. } => if value { TRUE } else { FALSE },
        Expression::Prefix { ref operator, ref right, .. } => {
            let right = eval_expr(right);
            eval_prefix_expr(operator, right)
        },
        _ => NULL
    }
}

fn eval_prefix_expr(op: &str, right: Object) -> Object {
    match op {
        "!" => eval_bang_operator_expr(right),
        "-" => eval_minus_prefix_operator_expr(right),
        _ => NULL
    }
}

fn eval_bang_operator_expr(right: Object) -> Object {
    match right {
        TRUE => FALSE,
        FALSE => TRUE,
        NULL => TRUE,
        _ => FALSE
    }
}

fn eval_minus_prefix_operator_expr(right: Object) -> Object {
    match right {
        Object::Integer(val) => Object::Integer(-val),
        _ => NULL,
    }
}

#[cfg(test)]
use super::lexer::Lexer;
#[cfg(test)]
use super::parser::Parser;

#[cfg(test)]
fn test_eval(input: &str) -> Object {
    let mut l = Lexer::new(input.to_string());
    let mut p = Parser::new(&mut l);
    let program = p.parse_program();

    eval(program)
}

#[cfg(test)]
fn check_integer_object(obj: Object, expected: i64) {
    match obj {
        Object::Integer(val) => {
            assert!(val == expected,
                    "Object has wrong value. Got {}, want {}",
                    val,
                    expected);
        },
        _ => panic!("Object is not an Integer. Got {:?}", obj)
    }
}

#[cfg(test)]
fn check_boolean_object(obj: Object, expected: bool) {
    match obj {
        Object::Boolean(val) => {
            assert!(val == expected,
                    "Object has wrong value. Got {}, want {}",
                    val,
                    expected);
        },
        _ => panic!("Object is not an Boolean. Got {:?}", obj)
    }
}

#[test]
fn test_eval_integer_expression() {
    let tests = vec![
        ("5", 5),
        ("10", 10),
        ("-5", -5),
        ("-10", -10)
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_integer_object(evaluated, expected);
    }
}

#[test]
fn test_eval_boolean_expression() {
    let tests = vec![
        ("true", true),
        ("false", false)
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_boolean_object(evaluated, expected);
    }
}

#[test]
fn test_bang_operator() {
    let tests = vec![
        ("!true", false),
        ("!false", true),
        ("!5", false),
        ("!!true", true),
        ("!!false", false),
        ("!!5", true),
    ];

    for (input, expected) in tests {
        let evaluated = test_eval(input);
        check_boolean_object(evaluated, expected);
    }
}<|endoftext|>


<commit_before>use std::sync::{Mutex, Arc};
use std::sync::atomics::{AtomicUint, SeqCst};

pub struct EWMA {
    pub uncounted: AtomicUint, // This tracks uncounted events
    alpha: f64,
    rate: Mutex<f64>,
    init: bool,
}

pub struct EWMASnapshot {
    value: f64
}

impl EWMASnapshot {
    fn rate(&self) -> f64 {
        return self.value;
    }
}

impl EWMA {
    pub fn rate(&self) -> f64 {
        let r = self.rate.lock();
        *r * (1e9 as f64)
    }

    pub fn snapshot(&self) -> EWMASnapshot {
        return EWMASnapshot{ value: self.rate() };
    }

    pub fn tick(&mut self) {
        let counter: uint = self.uncounted.load(SeqCst);

        self.uncounted.fetch_sub(counter, SeqCst); // Broken atm

        let mut rate = self.rate.lock();
        let i_rate = (counter as f64) / (5e9);

        if self.init {
            *rate += self.alpha * (i_rate - *rate);
        } else {
            self.init = true;
            *rate = i_rate;
        }

        rate.cond.signal();
    }

    pub fn update(&self, n: uint) {
        self.uncounted.fetch_add(n, SeqCst);
    }

    pub fn new(alpha: f64) -> EWMA {
        return EWMA{
            uncounted: AtomicUint::new(0u),
            alpha: alpha,
            rate: Mutex::new(0f64),
            init: false,
        }
    }

}

#[cfg(test)]
mod test {
    use ewma::EWMA;
    use std::num::Float;

    // Tick a minute
    fn elapse_minute(e: &mut EWMA) {
        for i in range(0i, 12i) {
            e.tick();
        }
    }

    // Returns whether the rate() is within 0.0001 of expected after ticking a minute
    fn within(e: &mut EWMA, expected: f64) -> bool {
        elapse_minute(e);
        let r: f64 = e.rate();
        (r - expected).abs() < 0.0001
    }

    #[test]
    fn ewma1() {
        let i = -5.0f64/60.0f64/1f64;
        let mut e = EWMA::new(1f64 - i.exp());
        e.update(3u);
        e.tick();

        let mut r: f64;

        // initial
        r = e.rate();
        assert_eq!(r, 0.6f64);

        // 1 minute
        assert_eq!(within(&mut e, 0.22072766470286553f64), true);

        // 2 minute
        assert_eq!(within(&mut e, 0.08120116994196772f64), true);

        // 3 minute
        assert_eq!(within(&mut e, 0.029872241020718428f64), true);

        // 4 minute
        assert_eq!(within(&mut e, 0.01098938333324054f64), true);

        // 5 minute
        assert_eq!(within(&mut e, 0.004042768199451294f64), true);

        // 6 minute
        assert_eq!(within(&mut e, 0.0014872513059998212f64), true);

        // 7 minute
        assert_eq!(within(&mut e, 0.0005471291793327122f64), true);

        // 8 minute
        assert_eq!(within(&mut e, 0.00020127757674150815f64), true);

        // 9 minute
        assert_eq!(within(&mut e, 7.404588245200814e-05f64), true);

        // 10 minute
        assert_eq!(within(&mut e, 2.7239957857491083e-05f64), true);

        // 11 minute
        assert_eq!(within(&mut e, 1.0021020474147462e-05f64), true);

        // 12 minute
        assert_eq!(within(&mut e, 3.6865274119969525e-06f64), true);

        // 13 minute
        assert_eq!(within(&mut e, 1.3561976441886433e-06f64), true);

        // 14 minute
        assert_eq!(within(&mut e, 4.989172314621449e-07f64), true);

        // 15 minute
        assert_eq!(within(&mut e, 1.8354139230109722e-07f64), true);
    }

    #[test]
    fn ewma5() {
        let i = -5.0f64/60.0f64/5f64;
        let mut e = EWMA::new(1f64 - i.exp());
        e.update(3u);
        e.tick();

        let r: f64 = e.rate();
        assert_eq!(r, 0.6f64);

        // 1 minute
        assert_eq!(within(&mut e, 0.49123845184678905f64), true);

                // 2 minute
        assert_eq!(within(&mut e, 0.4021920276213837f64), true);

        // 3 minute
        assert_eq!(within(&mut e, 0.32928698165641596f64), true);

        // 4 minute
        assert_eq!(within(&mut e, 0.269597378470333f64), true);

        // 5 minute
        assert_eq!(within(&mut e, 0.2207276647028654f64), true);

        // 6 minute
        assert_eq!(within(&mut e, 0.18071652714732128f64), true);

        // 7 minute
        assert_eq!(within(&mut e, 0.14795817836496392f64), true);

        // 8 minute
        assert_eq!(within(&mut e, 0.12113791079679326f64), true);

        // 9 minute
        assert_eq!(within(&mut e, 0.09917933293295193f64), true);

        // 10 minute
        assert_eq!(within(&mut e, 0.08120116994196763f64), true);

        // 11 minute
        assert_eq!(within(&mut e, 0.06648189501740036), true);

        // 12 minute
        assert_eq!(within(&mut e, 0.05443077197364752f64), true);

        // 13 minute
        assert_eq!(within(&mut e, 0.04456414692860035f64), true);

        // 14 minute
        assert_eq!(within(&mut e, 0.03648603757513079f64), true);

        // 15 minute
        assert_eq!(within(&mut e, 0.0298722410207183831020718428f64), true);
    }

        #[test]
    fn ewma15() {
        let i = -5.0f64/60.0f64/15f64;
        let mut e = EWMA::new(1f64 - i.exp());
        e.update(3u);
        e.tick();

        let r: f64 = e.rate();
        assert_eq!(r, 0.6f64);

        // 1 minute
        assert_eq!(within(&mut e, 0.5613041910189706f64), true);

        // 2 minute
        assert_eq!(within(&mut e, 0.5251039914257684f64), true);

        // 3 minute
        assert_eq!(within(&mut e, 0.4912384518467888184678905f64), true);

        // 4 minute
        assert_eq!(within(&mut e, 0.459557003018789f64), true);

        // 5 minute
        assert_eq!(within(&mut e, 0.4299187863442732f64), true);

        // 6 minute
        assert_eq!(within(&mut e, 0.4021920276213831f64), true);

        // 7 minute
        assert_eq!(within(&mut e, 0.37625345116383313f64), true);

        // 8 minute
        assert_eq!(within(&mut e, 0.3519877317060185f64), true);

        // 9 minute
        assert_eq!(within(&mut e, 0.3292869816564153165641596f64), true);

        // 10 minute
        assert_eq!(within(&mut e, 0.3080502714195546f64), true);

        // 11 minute
        assert_eq!(within(&mut e, 0.2881831806538789f64), true);

        // 12 minute
        assert_eq!(within(&mut e, 0.26959737847033216f64), true);

        // 13 minute
        assert_eq!(within(&mut e, 0.2522102307052083f64), true);

        // 14 minute
        assert_eq!(within(&mut e, 0.23594443252115815f64), true);

        // 15 minute
        assert_eq!(within(&mut e, 0.2207276647028646247028654470286553f64), true);
    }
}<commit_msg>Should be pub<commit_after>use std::sync::{Mutex, Arc};
use std::sync::atomics::{AtomicUint, SeqCst};

pub struct EWMA {
    pub uncounted: AtomicUint, // This tracks uncounted events
    alpha: f64,
    rate: Mutex<f64>,
    init: bool,
}

pub struct EWMASnapshot {
    value: f64
}

impl EWMASnapshot {
    pub fn rate(&self) -> f64 {
        return self.value;
    }
}

impl EWMA {
    pub fn rate(&self) -> f64 {
        let r = self.rate.lock();
        *r * (1e9 as f64)
    }

    pub fn snapshot(&self) -> EWMASnapshot {
        return EWMASnapshot{ value: self.rate() };
    }

    pub fn tick(&mut self) {
        let counter: uint = self.uncounted.load(SeqCst);

        self.uncounted.fetch_sub(counter, SeqCst); // Broken atm

        let mut rate = self.rate.lock();
        let i_rate = (counter as f64) / (5e9);

        if self.init {
            *rate += self.alpha * (i_rate - *rate);
        } else {
            self.init = true;
            *rate = i_rate;
        }

        rate.cond.signal();
    }

    pub fn update(&self, n: uint) {
        self.uncounted.fetch_add(n, SeqCst);
    }

    pub fn new(alpha: f64) -> EWMA {
        return EWMA{
            uncounted: AtomicUint::new(0u),
            alpha: alpha,
            rate: Mutex::new(0f64),
            init: false,
        }
    }

}

#[cfg(test)]
mod test {
    use ewma::EWMA;
    use std::num::Float;

    // Tick a minute
    fn elapse_minute(e: &mut EWMA) {
        for i in range(0i, 12i) {
            e.tick();
        }
    }

    // Returns whether the rate() is within 0.0001 of expected after ticking a minute
    fn within(e: &mut EWMA, expected: f64) -> bool {
        elapse_minute(e);
        let r: f64 = e.rate();
        (r - expected).abs() < 0.0001
    }

    #[test]
    fn ewma1() {
        let i = -5.0f64/60.0f64/1f64;
        let mut e = EWMA::new(1f64 - i.exp());
        e.update(3u);
        e.tick();

        let mut r: f64;

        // initial
        r = e.rate();
        assert_eq!(r, 0.6f64);

        // 1 minute
        assert_eq!(within(&mut e, 0.22072766470286553f64), true);

        // 2 minute
        assert_eq!(within(&mut e, 0.08120116994196772f64), true);

        // 3 minute
        assert_eq!(within(&mut e, 0.029872241020718428f64), true);

        // 4 minute
        assert_eq!(within(&mut e, 0.01098938333324054f64), true);

        // 5 minute
        assert_eq!(within(&mut e, 0.004042768199451294f64), true);

        // 6 minute
        assert_eq!(within(&mut e, 0.0014872513059998212f64), true);

        // 7 minute
        assert_eq!(within(&mut e, 0.0005471291793327122f64), true);

        // 8 minute
        assert_eq!(within(&mut e, 0.00020127757674150815f64), true);

        // 9 minute
        assert_eq!(within(&mut e, 7.404588245200814e-05f64), true);

        // 10 minute
        assert_eq!(within(&mut e, 2.7239957857491083e-05f64), true);

        // 11 minute
        assert_eq!(within(&mut e, 1.0021020474147462e-05f64), true);

        // 12 minute
        assert_eq!(within(&mut e, 3.6865274119969525e-06f64), true);

        // 13 minute
        assert_eq!(within(&mut e, 1.3561976441886433e-06f64), true);

        // 14 minute
        assert_eq!(within(&mut e, 4.989172314621449e-07f64), true);

        // 15 minute
        assert_eq!(within(&mut e, 1.8354139230109722e-07f64), true);
    }

    #[test]
    fn ewma5() {
        let i = -5.0f64/60.0f64/5f64;
        let mut e = EWMA::new(1f64 - i.exp());
        e.update(3u);
        e.tick();

        let r: f64 = e.rate();
        assert_eq!(r, 0.6f64);

        // 1 minute
        assert_eq!(within(&mut e, 0.49123845184678905f64), true);

                // 2 minute
        assert_eq!(within(&mut e, 0.4021920276213837f64), true);

        // 3 minute
        assert_eq!(within(&mut e, 0.32928698165641596f64), true);

        // 4 minute
        assert_eq!(within(&mut e, 0.269597378470333f64), true);

        // 5 minute
        assert_eq!(within(&mut e, 0.2207276647028654f64), true);

        // 6 minute
        assert_eq!(within(&mut e, 0.18071652714732128f64), true);

        // 7 minute
        assert_eq!(within(&mut e, 0.14795817836496392f64), true);

        // 8 minute
        assert_eq!(within(&mut e, 0.12113791079679326f64), true);

        // 9 minute
        assert_eq!(within(&mut e, 0.09917933293295193f64), true);

        // 10 minute
        assert_eq!(within(&mut e, 0.08120116994196763f64), true);

        // 11 minute
        assert_eq!(within(&mut e, 0.06648189501740036), true);

        // 12 minute
        assert_eq!(within(&mut e, 0.05443077197364752f64), true);

        // 13 minute
        assert_eq!(within(&mut e, 0.04456414692860035f64), true);

        // 14 minute
        assert_eq!(within(&mut e, 0.03648603757513079f64), true);

        // 15 minute
        assert_eq!(within(&mut e, 0.0298722410207183831020718428f64), true);
    }

        #[test]
    fn ewma15() {
        let i = -5.0f64/60.0f64/15f64;
        let mut e = EWMA::new(1f64 - i.exp());
        e.update(3u);
        e.tick();

        let r: f64 = e.rate();
        assert_eq!(r, 0.6f64);

        // 1 minute
        assert_eq!(within(&mut e, 0.5613041910189706f64), true);

        // 2 minute
        assert_eq!(within(&mut e, 0.5251039914257684f64), true);

        // 3 minute
        assert_eq!(within(&mut e, 0.4912384518467888184678905f64), true);

        // 4 minute
        assert_eq!(within(&mut e, 0.459557003018789f64), true);

        // 5 minute
        assert_eq!(within(&mut e, 0.4299187863442732f64), true);

        // 6 minute
        assert_eq!(within(&mut e, 0.4021920276213831f64), true);

        // 7 minute
        assert_eq!(within(&mut e, 0.37625345116383313f64), true);

        // 8 minute
        assert_eq!(within(&mut e, 0.3519877317060185f64), true);

        // 9 minute
        assert_eq!(within(&mut e, 0.3292869816564153165641596f64), true);

        // 10 minute
        assert_eq!(within(&mut e, 0.3080502714195546f64), true);

        // 11 minute
        assert_eq!(within(&mut e, 0.2881831806538789f64), true);

        // 12 minute
        assert_eq!(within(&mut e, 0.26959737847033216f64), true);

        // 13 minute
        assert_eq!(within(&mut e, 0.2522102307052083f64), true);

        // 14 minute
        assert_eq!(within(&mut e, 0.23594443252115815f64), true);

        // 15 minute
        assert_eq!(within(&mut e, 0.2207276647028646247028654470286553f64), true);
    }
}<|endoftext|>


<commit_before>//! FLIC implementation.

use std::fs::File;
use std::io::{Cursor,Read,Seek,SeekFrom};
use std::path::{Path,PathBuf};
use byteorder::LittleEndian as LE;
use byteorder::ReadBytesExt;

use ::{FlicError,FlicResult,RasterMut};
use codec::*;

/// Magic for a FLI file - Original Animator FLI Files.
///
/// This animation file format is limited to 320x200 resolution.  It
/// is the main animation file format of the original Animator, and is
/// still used by Animator Pro for creating 320x200 animations.  The
/// file structure is very similar to that of a FLC file.  A FLI file
/// does not contain a prefix chunk, and does not use FLI_PSTAMP or
/// FLI_SS2 data encoding in the frame chunks.
///
///   Offset | Length |   Name   | Description
///   ------:| ------:|:--------:| -----------------------------------
///        0 |      4 |   size   | The size of the entire animation file, including this file header.
///        4 |      2 |   magic  | File format identifier.  Always 0xAF11.
///        6 |      2 |  frames  | Number of frames in the FLIC.  This count does not include the ring frame.  FLI files have a maximum length of 4000 frames.
///        8 |      2 |   width  | Screen width in pixels.  This is always 320 in a FLI file.
///       10 |      2 |  height  | Screen height in pixels.  This is always 200 in a FLI file.
///       12 |      2 |   depth  | Bits per pixel (always 8).
///       14 |      2 |   flags  | Always zero in a FLI file.
///       16 |      2 |   speed  | Number of jiffies to delay between each frame during playback.  A jiffy is 1/70 of a second.
///       18 |    110 | reserved | Unused space, set to zeroes.
const FLIH_MAGIC: u16 = 0xAF11;

/// FLIC animation, with a File handle.
///
/// Opens and holds onto the file handle until it is dropped.
#[allow(dead_code)]
pub struct FlicFile {
    hdr: FlicHeader,
    frame_hdr: Vec<FlicFrame>,
    frame: usize,

    filename: PathBuf,
    file: File,
}

/// Size of a FLIC file header on disk.
///
/// A FLIC file begins with a 128-byte header, described below.  All
/// lengths and offsets are in bytes.  All values stored in the header
/// fields are unsigned.
const SIZE_OF_FLIC_HEADER: usize = 128;

/// FLIC header.
#[allow(dead_code)]
struct FlicHeader {
    size: u32,
    frame_count: u16,
    w: u16,
    h: u16,
    speed_jiffies: u16,
}

/// Magic for a FLIC frame - FLIC Frame Chunks.
///
/// Frame chunks contain the pixel and color data for the animation.
/// A frame chunk may contain multiple subordinate chunks, each
/// containing a different type of data for the current frame.  Each
/// frame chunk starts with a 16-byte header that describes the
/// contents of the frame:
///
///   Offset | Length |   Name   | Description
///   ------:| ------:|:--------:| -----------------------------------
///        0 |      4 |   size   | The size of the frame chunk, including this header and all subordinate chunks that follow.
///        4 |      2 |   type   | Frame chunk identifier.
///        6 |      2 |  chunks  | Number of subordinate chunks in the frame chunk.
///        8 |      8 | reserved | Unused space, set to zeroes.
const FCID_FRAME: u16 = 0xF1FA; // also: FLIF_MAGIC.

/// Size of a FLIC frame header on disk.
const SIZE_OF_FLIC_FRAME: usize = 16;

/// FLIC frame header.
#[allow(dead_code)]
struct FlicFrame {
    chunks: Vec<ChunkId>,
}

/// Size of a chunk header on disk.
///
/// Immediately following the frame header are the frame's subordinate
/// data chunks.  When the chunks count in the frame header is zero,
/// it indicates that this frame is identical to the previous frame.
/// This implies that nochange is made to the screen or color palette,
/// but the appropriate delay is still inserted during playback.
///
/// Each data chunk within a frame chunk is formatted as follows:
///
///   Offset | Length | Name | Description
///   ------:| ------:|:----:| ---------------------------------------
///        0 |      4 | size | The size of the chunk, including this header.
///        4 |      2 | type | Data type identifier.
///        6 | size-6 | data | The color or pixel data.
///
/// The type values in the chunk headers indicate what type of
/// graphics data the chunk contains and which compression method was
/// used to encode the data.
const SIZE_OF_CHUNK: usize = 6;

/// Chunk header.
#[allow(dead_code)]
struct ChunkId {
    // Note: offset to the data.
    offset: u64,

    // Note: number of bytes in the data, excluding the chunk header.
    size: u32,

    magic: u16,
}

/// Record containing playback information.
pub struct FlicPlaybackResult {
    pub ended: bool,
    pub looped: bool,
    pub palette_updated: bool,
}

/*--------------------------------------------------------------*/

impl FlicFile {
    /// Open a FLIC file.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::path::Path;
    ///
    /// flic::FlicFile::open(Path::new("ex.fli"));
    /// ```
    pub fn open(filename: &Path)
            -> FlicResult<Self> {
        if !filename.exists() {
            return Err(FlicError::NoFile);
        } else if !filename.is_file() {
            return Err(FlicError::NotARegularFile);
        }

        let mut file = try!(File::open(filename));

        let hdr = try!(read_flic_header(&mut file));
        let frame_hdr = try!(read_frame_headers(&mut file, &hdr));

        Ok(FlicFile {
            hdr: hdr,
            frame_hdr: frame_hdr,
            frame: 0,

            filename: filename.to_path_buf(),
            file: file,
        })
    }

    /// Get the next frame number.
    pub fn frame(&self) -> u16 {
        self.frame as u16
    }

    /// Get the frame count, not including the ring frame.
    pub fn frame_count(&self) -> u16 {
        self.hdr.frame_count
    }

    /// Get the FLIC width.
    pub fn width(&self) -> u16 {
        self.hdr.w
    }

    /// Get the FLIC height.
    pub fn height(&self) -> u16 {
        self.hdr.h
    }

    /// Number of jiffies to delay between each frame during playback.
    /// A jiffy is 1/70 of a second.
    pub fn speed_jiffies(&self) -> u16 {
        self.hdr.speed_jiffies
    }

    /// Decode the next frame in the FLIC.
    ///
    /// The raster buffer must contain the previous frame.
    /// The FLIC file will loop when it reaches the last frame.
    ///
    /// Returns a record indicating what was processed.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::path::Path;
    ///
    /// if let Ok(ref mut flic) = flic::FlicFile::open(Path::new("ex.fli")) {
    ///     const SCREEN_W: usize = 320;
    ///     const SCREEN_H: usize = 200;
    ///     const NUM_COLS: usize = 256;
    ///     let mut buf = [0; SCREEN_W * SCREEN_H];
    ///     let mut pal = [0; 3 * NUM_COLS];
    ///     let mut raster = flic::RasterMut::new(SCREEN_W, SCREEN_H, &mut buf, &mut pal);
    ///
    ///     let res = flic.read_next_frame(&mut raster);
    /// }
    /// ```
    pub fn read_next_frame(&mut self, dst: &mut RasterMut)
            -> FlicResult<FlicPlaybackResult> {
        let mut res = FlicPlaybackResult {
            ended: false,
            looped: false,
            palette_updated: false,
        };

        if (self.hdr.w as usize != dst.w) || (self.hdr.h as usize != dst.h) {
            return Err(FlicError::WrongResolution);
        }

        let frame = &self.frame_hdr[self.frame];
        for chunk in frame.chunks.iter() {
            try!(self.file.seek(SeekFrom::Start(chunk.offset)));

            let mut buf = vec![0; chunk.size as usize];
            try!(self.file.read_exact(&mut buf));

            try!(decode_chunk(chunk.magic, &buf, dst));

            res.palette_updated = res.palette_updated
                    || chunk_modifies_palette(chunk.magic);
        }

        if self.frame + 1 >= self.frame_hdr.len() {
            // Skip to second frame, since FLIC animations include a ring frame.
            self.frame = 1;
            res.looped = true;
        } else {
            self.frame = self.frame + 1;
        }

        if self.frame + 1 >= self.frame_hdr.len() {
            res.ended = true;
        }

        Ok(res)
    }
}

/*--------------------------------------------------------------*/

/// Read the FLIC's header.
fn read_flic_header(file: &mut File)
        -> FlicResult<FlicHeader> {
    let mut buf = [0; SIZE_OF_FLIC_HEADER];
    try!(file.read_exact(&mut buf));

    let mut r = Cursor::new(&buf[..]);
    let size = try!(r.read_u32::<LE>());
    let magic = try!(r.read_u16::<LE>());

    if magic != FLIH_MAGIC {
        return Err(FlicError::BadMagic);
    }

    let frame_count = try!(r.read_u16::<LE>());
    let width = try!(r.read_u16::<LE>());
    let height = try!(r.read_u16::<LE>());
    let _bpp = try!(r.read_u16::<LE>());
    let _flags = try!(r.read_u16::<LE>());
    let jiffy_speed = try!(r.read_u16::<LE>());

    match r.seek(SeekFrom::Current(110)) {
        Ok(128) => (),
        _ => unreachable!(),
    };

    // Animator 1's FLIC files are always 320x200.
    if width != 320 || height != 200 {
        return Err(FlicError::WrongResolution);
    }
    if frame_count <= 0 {
        return Err(FlicError::Corrupted);
    }

    Ok(FlicHeader{
        size: size,
        frame_count: frame_count,
        w: width,
        h: height,
        speed_jiffies: jiffy_speed,
    })
}

/// Read all of the FLIC's frame headers.
fn read_frame_headers(file: &mut File, hdr: &FlicHeader)
        -> FlicResult<Vec<FlicFrame>> {
    let mut offset = SIZE_OF_FLIC_HEADER as u64;
    let mut frames = Vec::new();

    // Add 1 to frame count to account for the ring frame.
    for frame_num in 0..(hdr.frame_count + 1) {
        try!(file.seek(SeekFrom::Start(offset)));

        let mut buf = [0; SIZE_OF_FLIC_FRAME];
        try!(file.read_exact(&mut buf));

        let mut r = Cursor::new(&buf[..]);
        let size = try!(r.read_u32::<LE>());
        let magic = try!(r.read_u16::<LE>());
        let num_chunks = try!(r.read_u16::<LE>()) as usize;

        if size < (SIZE_OF_FLIC_FRAME as u32)
                || offset + (size as u64) > (hdr.size as u64) {
            return Err(FlicError::Corrupted);
        }
        if magic != FCID_FRAME {
            return Err(FlicError::BadMagic);
        }

        let chunks = try!(read_chunk_headers(file,
                frame_num, offset, size, num_chunks));
        assert_eq!(chunks.len(), num_chunks);

        frames.push(FlicFrame{
            chunks: chunks,
        });

        offset = offset + size as u64;
    }

    Ok(frames)
}

/// Read all of the frame's chunk headers.
fn read_chunk_headers(file: &mut File,
        frame_num: u16, frame_offset: u64, frame_size: u32, num_chunks: usize)
        -> FlicResult<Vec<ChunkId>> {
    let mut chunks = Vec::new();
    let mut offset = frame_offset + SIZE_OF_FLIC_FRAME as u64;

    for _ in 0..num_chunks {
        try!(file.seek(SeekFrom::Start(offset)));

        let mut buf = [0; SIZE_OF_CHUNK];
        try!(file.read_exact(&mut buf));

        let mut r = Cursor::new(&buf[..]);
        let size = try!(r.read_u32::<LE>());
        let magic = try!(r.read_u16::<LE>());

        if !(SIZE_OF_CHUNK as u32 <= size && size <= frame_size) {
            return Err(FlicError::Corrupted);
        }

        match magic {
            FLI_COLOR64 | FLI_LC | FLI_BLACK | FLI_BRUN | FLI_COPY => (),

            _ => println!("Warning: frame {} - unrecognised chunk type {}",
                    frame_num, magic),
        }

        chunks.push(ChunkId {
            offset: offset + SIZE_OF_CHUNK as u64,
            size: size - SIZE_OF_CHUNK as u32,
            magic: magic,
        });

        offset = offset + size as u64;
    }

    Ok(chunks)
}
<commit_msg>Add workarounds for some Animator bugs.<commit_after>//! FLIC implementation.

use std::fs::File;
use std::io::{Cursor,Read,Seek,SeekFrom};
use std::path::{Path,PathBuf};
use byteorder::LittleEndian as LE;
use byteorder::ReadBytesExt;

use ::{FlicError,FlicResult,RasterMut};
use codec::*;

/// Magic for a FLI file - Original Animator FLI Files.
///
/// This animation file format is limited to 320x200 resolution.  It
/// is the main animation file format of the original Animator, and is
/// still used by Animator Pro for creating 320x200 animations.  The
/// file structure is very similar to that of a FLC file.  A FLI file
/// does not contain a prefix chunk, and does not use FLI_PSTAMP or
/// FLI_SS2 data encoding in the frame chunks.
///
///   Offset | Length |   Name   | Description
///   ------:| ------:|:--------:| -----------------------------------
///        0 |      4 |   size   | The size of the entire animation file, including this file header.
///        4 |      2 |   magic  | File format identifier.  Always 0xAF11.
///        6 |      2 |  frames  | Number of frames in the FLIC.  This count does not include the ring frame.  FLI files have a maximum length of 4000 frames.
///        8 |      2 |   width  | Screen width in pixels.  This is always 320 in a FLI file.
///       10 |      2 |  height  | Screen height in pixels.  This is always 200 in a FLI file.
///       12 |      2 |   depth  | Bits per pixel (always 8).
///       14 |      2 |   flags  | Always zero in a FLI file.
///       16 |      2 |   speed  | Number of jiffies to delay between each frame during playback.  A jiffy is 1/70 of a second.
///       18 |    110 | reserved | Unused space, set to zeroes.
const FLIH_MAGIC: u16 = 0xAF11;

/// FLIC animation, with a File handle.
///
/// Opens and holds onto the file handle until it is dropped.
#[allow(dead_code)]
pub struct FlicFile {
    hdr: FlicHeader,
    frame_hdr: Vec<FlicFrame>,
    frame: usize,

    filename: PathBuf,
    file: File,
}

/// Size of a FLIC file header on disk.
///
/// A FLIC file begins with a 128-byte header, described below.  All
/// lengths and offsets are in bytes.  All values stored in the header
/// fields are unsigned.
const SIZE_OF_FLIC_HEADER: usize = 128;

/// FLIC header.
#[allow(dead_code)]
struct FlicHeader {
    size: u32,
    frame_count: u16,
    w: u16,
    h: u16,
    speed_jiffies: u16,
}

/// Magic for a FLIC frame - FLIC Frame Chunks.
///
/// Frame chunks contain the pixel and color data for the animation.
/// A frame chunk may contain multiple subordinate chunks, each
/// containing a different type of data for the current frame.  Each
/// frame chunk starts with a 16-byte header that describes the
/// contents of the frame:
///
///   Offset | Length |   Name   | Description
///   ------:| ------:|:--------:| -----------------------------------
///        0 |      4 |   size   | The size of the frame chunk, including this header and all subordinate chunks that follow.
///        4 |      2 |   type   | Frame chunk identifier.
///        6 |      2 |  chunks  | Number of subordinate chunks in the frame chunk.
///        8 |      8 | reserved | Unused space, set to zeroes.
const FCID_FRAME: u16 = 0xF1FA; // also: FLIF_MAGIC.

/// Size of a FLIC frame header on disk.
const SIZE_OF_FLIC_FRAME: usize = 16;

/// FLIC frame header.
#[allow(dead_code)]
struct FlicFrame {
    chunks: Vec<ChunkId>,
}

/// Size of a chunk header on disk.
///
/// Immediately following the frame header are the frame's subordinate
/// data chunks.  When the chunks count in the frame header is zero,
/// it indicates that this frame is identical to the previous frame.
/// This implies that nochange is made to the screen or color palette,
/// but the appropriate delay is still inserted during playback.
///
/// Each data chunk within a frame chunk is formatted as follows:
///
///   Offset | Length | Name | Description
///   ------:| ------:|:----:| ---------------------------------------
///        0 |      4 | size | The size of the chunk, including this header.
///        4 |      2 | type | Data type identifier.
///        6 | size-6 | data | The color or pixel data.
///
/// The type values in the chunk headers indicate what type of
/// graphics data the chunk contains and which compression method was
/// used to encode the data.
const SIZE_OF_CHUNK: usize = 6;

/// Chunk header.
#[allow(dead_code)]
struct ChunkId {
    // Note: offset to the data.
    offset: u64,

    // Note: number of bytes in the data, excluding the chunk header.
    size: u32,

    magic: u16,
}

/// Record containing playback information.
pub struct FlicPlaybackResult {
    pub ended: bool,
    pub looped: bool,
    pub palette_updated: bool,
}

/*--------------------------------------------------------------*/

impl FlicFile {
    /// Open a FLIC file.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::path::Path;
    ///
    /// flic::FlicFile::open(Path::new("ex.fli"));
    /// ```
    pub fn open(filename: &Path)
            -> FlicResult<Self> {
        if !filename.exists() {
            return Err(FlicError::NoFile);
        } else if !filename.is_file() {
            return Err(FlicError::NotARegularFile);
        }

        let mut file = try!(File::open(filename));

        let hdr = try!(read_flic_header(&mut file));
        let frame_hdr = try!(read_frame_headers(&mut file, &hdr));

        Ok(FlicFile {
            hdr: hdr,
            frame_hdr: frame_hdr,
            frame: 0,

            filename: filename.to_path_buf(),
            file: file,
        })
    }

    /// Get the next frame number.
    pub fn frame(&self) -> u16 {
        self.frame as u16
    }

    /// Get the frame count, not including the ring frame.
    pub fn frame_count(&self) -> u16 {
        self.hdr.frame_count
    }

    /// Get the FLIC width.
    pub fn width(&self) -> u16 {
        self.hdr.w
    }

    /// Get the FLIC height.
    pub fn height(&self) -> u16 {
        self.hdr.h
    }

    /// Number of jiffies to delay between each frame during playback.
    /// A jiffy is 1/70 of a second.
    pub fn speed_jiffies(&self) -> u16 {
        self.hdr.speed_jiffies
    }

    /// Decode the next frame in the FLIC.
    ///
    /// The raster buffer must contain the previous frame.
    /// The FLIC file will loop when it reaches the last frame.
    ///
    /// Returns a record indicating what was processed.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::path::Path;
    ///
    /// if let Ok(ref mut flic) = flic::FlicFile::open(Path::new("ex.fli")) {
    ///     const SCREEN_W: usize = 320;
    ///     const SCREEN_H: usize = 200;
    ///     const NUM_COLS: usize = 256;
    ///     let mut buf = [0; SCREEN_W * SCREEN_H];
    ///     let mut pal = [0; 3 * NUM_COLS];
    ///     let mut raster = flic::RasterMut::new(SCREEN_W, SCREEN_H, &mut buf, &mut pal);
    ///
    ///     let res = flic.read_next_frame(&mut raster);
    /// }
    /// ```
    pub fn read_next_frame(&mut self, dst: &mut RasterMut)
            -> FlicResult<FlicPlaybackResult> {
        let mut res = FlicPlaybackResult {
            ended: false,
            looped: false,
            palette_updated: false,
        };

        if (self.hdr.w as usize != dst.w) || (self.hdr.h as usize != dst.h) {
            return Err(FlicError::WrongResolution);
        }

        let frame = &self.frame_hdr[self.frame];
        for chunk in frame.chunks.iter() {
            try!(self.file.seek(SeekFrom::Start(chunk.offset)));

            let mut buf = vec![0; chunk.size as usize];
            try!(self.file.read_exact(&mut buf));

            try!(decode_chunk(chunk.magic, &buf, dst));

            res.palette_updated = res.palette_updated
                    || chunk_modifies_palette(chunk.magic);
        }

        if self.frame + 1 >= self.frame_hdr.len() {
            // Skip to second frame, since FLIC animations include a ring frame.
            self.frame = 1;
            res.looped = true;
        } else {
            self.frame = self.frame + 1;
        }

        if self.frame + 1 >= self.frame_hdr.len() {
            res.ended = true;
        }

        Ok(res)
    }
}

/*--------------------------------------------------------------*/

/// Read the FLIC's header.
fn read_flic_header(file: &mut File)
        -> FlicResult<FlicHeader> {
    let mut buf = [0; SIZE_OF_FLIC_HEADER];
    try!(file.read_exact(&mut buf));

    let mut r = Cursor::new(&buf[..]);
    let size = try!(r.read_u32::<LE>());
    let magic = try!(r.read_u16::<LE>());

    if magic != FLIH_MAGIC {
        return Err(FlicError::BadMagic);
    }

    let frame_count = try!(r.read_u16::<LE>());
    let width = try!(r.read_u16::<LE>());
    let height = try!(r.read_u16::<LE>());
    let _bpp = try!(r.read_u16::<LE>());
    let _flags = try!(r.read_u16::<LE>());
    let jiffy_speed = try!(r.read_u16::<LE>());

    match r.seek(SeekFrom::Current(110)) {
        Ok(128) => (),
        _ => unreachable!(),
    };

    // Animator 1's FLIC files are always 320x200.
    if width != 320 || height != 200 {
        return Err(FlicError::WrongResolution);
    }
    if frame_count <= 0 {
        return Err(FlicError::Corrupted);
    }

    Ok(FlicHeader{
        size: size,
        frame_count: frame_count,
        w: width,
        h: height,
        speed_jiffies: jiffy_speed,
    })
}

/// Read all of the FLIC's frame headers.
fn read_frame_headers(file: &mut File, hdr: &FlicHeader)
        -> FlicResult<Vec<FlicFrame>> {
    let mut offset = SIZE_OF_FLIC_HEADER as u64;
    let mut frames = Vec::new();

    // Add 1 to frame count to account for the ring frame.
    for frame_num in 0..(hdr.frame_count + 1) {
        try!(file.seek(SeekFrom::Start(offset)));

        let mut buf = [0; SIZE_OF_FLIC_FRAME];
        try!(file.read_exact(&mut buf));

        let mut r = Cursor::new(&buf[..]);
        let size = try!(r.read_u32::<LE>());
        let magic = try!(r.read_u16::<LE>());
        let num_chunks = try!(r.read_u16::<LE>()) as usize;

        if size < (SIZE_OF_FLIC_FRAME as u32)
                || offset + (size as u64) > (hdr.size as u64) {
            return Err(FlicError::Corrupted);
        }
        if magic != FCID_FRAME {
            return Err(FlicError::BadMagic);
        }

        let chunks = try!(read_chunk_headers(file, hdr,
                frame_num, offset, size, num_chunks));
        assert_eq!(chunks.len(), num_chunks);

        // Note: Animator forces chunk sizes to be even.  However,
        // Animator 1 did not update the frame header size
        // accordingly.  This resulted in lost data.
        if num_chunks > 0 {
            let position = chunks[num_chunks - 1].offset + chunks[num_chunks - 1].size as u64;
            let expected = offset + size as u64;
            if position > expected {
                println!("Warning: frame {} reads too much - current offset={}, expected offset={}",
                         frame_num, position, expected);
            } else if position < expected {
                println!("Warning: frame {} reads too little - current offset={}, expected offset={}",
                         frame_num, position, expected);
            }
        }

        frames.push(FlicFrame{
            chunks: chunks,
        });

        offset = offset + size as u64;
    }

    Ok(frames)
}

/// Read all of the frame's chunk headers.
fn read_chunk_headers(file: &mut File, hdr: &FlicHeader,
        frame_num: u16, frame_offset: u64, frame_size: u32, num_chunks: usize)
        -> FlicResult<Vec<ChunkId>> {
    let mut chunks = Vec::new();
    let mut offset = frame_offset + SIZE_OF_FLIC_FRAME as u64;

    for _ in 0..num_chunks {
        try!(file.seek(SeekFrom::Start(offset)));

        let mut buf = [0; SIZE_OF_CHUNK];
        try!(file.read_exact(&mut buf));

        let mut r = Cursor::new(&buf[..]);
        let size = try!(r.read_u32::<LE>());
        let magic = try!(r.read_u16::<LE>());

        if !(SIZE_OF_CHUNK as u32 <= size && size <= frame_size) {
            return Err(FlicError::Corrupted);
        }

        let mut size2 = size;

        match magic {
            // A bug in Animator and Animator Pro caused FLI_COPY
            // chunks have size = size of data + 4 (size of pointer)
            // instead of size of data + 6 (size of chunk header).
            // The data was still written to disk; only the chunk's
            // size is incorrect.
            FLI_COPY => {
                if size == hdr.w as u32 * hdr.h as u32 + 4 {
                    size2 = hdr.w as u32 * hdr.h as u32 + 6;
                    println!("Warning: frame {} - FLI_COPY has wrong size",
                            frame_num);
                }
            },

            FLI_COLOR64 | FLI_LC | FLI_BLACK | FLI_BRUN => (),

            _ => println!("Warning: frame {} - unrecognised chunk type {}",
                    frame_num, magic),
        }

        chunks.push(ChunkId {
            offset: offset + SIZE_OF_CHUNK as u64,
            size: size2 - SIZE_OF_CHUNK as u32,
            magic: magic,
        });

        offset = offset + size as u64;
    }

    Ok(chunks)
}
<|endoftext|>


<commit_before>use sha2::{HashFunction, MAX_DIGEST_SIZE, Sha224, Sha256, Sha384, Sha512};

const IPAD: u8 = 0x36;
const OPAD: u8 = 0x5c;

pub struct Hmac<H> {
    inner_hash_function: H,
    outer_hash_function: H,
}

macro_rules! impl_wrapper { ($function:ident, $key:expr, $message:expr) => {{
    let mut digest = [0; $function::DIGEST_SIZE];
    let mut hmac = Hmac::<$function>::new($key);
    hmac.update($message);
    hmac.write_digest(&mut digest);
    digest
}}}

pub fn hmac_sha512(key: &[u8], message: &[u8]) -> [u8; Sha512::DIGEST_SIZE] {
    impl_wrapper!(Sha512, key, message)
}

pub fn hmac_sha384(key: &[u8], message: &[u8]) -> [u8; Sha384::DIGEST_SIZE] {
    impl_wrapper!(Sha384, key, message)
}

pub fn hmac_sha256(key: &[u8], message: &[u8]) -> [u8; Sha256::DIGEST_SIZE] {
    impl_wrapper!(Sha256, key, message)
}

pub fn hmac_sha224(key: &[u8], message: &[u8]) -> [u8; Sha224::DIGEST_SIZE] {
    impl_wrapper!(Sha224, key, message)
}

impl<H: HashFunction> Hmac<H> {
    pub fn new(key: &[u8]) -> Self {
        let mut hashed_key;
        let new_key;
        if key.len() > H::BLOCK_SIZE {
            hashed_key = [0; MAX_DIGEST_SIZE];
            let mut hash_function = H::default();
            hash_function.update(key);
            hash_function.write_digest(&mut hashed_key[..H::DIGEST_SIZE]);
            new_key = &hashed_key[..H::DIGEST_SIZE];
        } else {
            new_key = key;
        }
        Self {
            inner_hash_function: Self::keyed_hash_function(new_key, IPAD),
            outer_hash_function: Self::keyed_hash_function(new_key, OPAD),
        }
    }

    pub fn update(&mut self, input: &[u8]) {
        self.inner_hash_function.update(input);
    }

    pub fn write_digest(mut self, output: &mut [u8]) {
        self.inner_hash_function.write_digest(output);
        self.outer_hash_function.update(output);
        self.outer_hash_function.write_digest(output);
    }

    fn keyed_hash_function(key: &[u8], pad: u8) -> H {
        let mut hash_function = H::default();
        for byte in key {
            hash_function.update(&[byte ^ pad]);
        }
        for _ in key.len()..H::BLOCK_SIZE {
            hash_function.update(&[pad]);
        }
        hash_function
    }
}

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

    fn check(exp512: &str, exp384: &str, exp256: &str, exp224: &str, key: &[u8], data: &[u8]) {
        let expected = h2b(exp512);
        let mut actual = hmac_sha512(key, data);
        assert_eq!(expected, actual.to_vec());

        let mut hmac = Hmac::<Sha512>::new(key);
        for word in data.chunks(4) {
            hmac.update(word);
        }
        hmac.write_digest(&mut actual);
        assert_eq!(expected, actual.to_vec());

        let expected = h2b(exp384);
        let mut actual = hmac_sha384(key, data);
        assert_eq!(expected, actual.to_vec());

        let mut hmac = Hmac::<Sha384>::new(key);
        for word in data.chunks(4) {
            hmac.update(word);
        }
        hmac.write_digest(&mut actual);
        assert_eq!(expected, actual.to_vec());

        let expected = h2b(exp256);
        let mut actual = hmac_sha256(key, data);
        assert_eq!(expected, actual.to_vec());

        let mut hmac = Hmac::<Sha256>::new(key);
        for word in data.chunks(4) {
            hmac.update(word);
        }
        hmac.write_digest(&mut actual);
        assert_eq!(expected, actual.to_vec());

        let expected = h2b(exp224);
        let mut actual = hmac_sha224(key, data);
        assert_eq!(expected, actual.to_vec());

        let mut hmac = Hmac::<Sha224>::new(key);
        for word in data.chunks(4) {
            hmac.update(word);
        }
        hmac.write_digest(&mut actual);
        assert_eq!(expected, actual.to_vec());
    }

    #[test]
    fn test_digest() {
        let key = h2b("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
        let data = b"Hi There";
        let exp512 = "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\
                      daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854";
        let exp384 = "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59c\
                      faea9ea9076ede7f4af152e8b2fa9cb6";
        let exp256 = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7";
        let exp224 = "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22";
        check(exp512, exp384, exp256, exp224, &key, data);

        let key = b"Jefe";
        let data = b"what do ya want for nothing?";
        let exp512 = "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\
                      9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737";
        let exp384 = "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e\
                      8e2240ca5e69e2c78b3239ecfab21649";
        let exp256 = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843";
        let exp224 = "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44";
        check(exp512, exp384, exp256, exp224, key, data);

        let key = [0xaa; 20];
        let data = [0xdd; 50];
        let exp512 = "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39\
                      bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb";
        let exp384 = "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b\
                      2a5ab39dc13814b94e3ab6e101a34f27";
        let exp256 = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe";
        let exp224 = "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea";
        check(exp512, exp384, exp256, exp224, &key, &data);

        let key: Vec<_> = (0x01..0x1a).collect();
        let data = [0xcd; 50];
        let exp512 = "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db\
                      a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd";
        let exp384 = "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e\
                      6801dd23c4a7d679ccf8a386c674cffb";
        let exp256 = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b";
        let exp224 = "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a";
        check(exp512, exp384, exp256, exp224, &key, &data);

        let key = [0xaa; 131];
        let data = b"Test Using Larger Than Block-Size Key - Hash Key First";
        let exp512 = "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352\
                      6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598";
        let exp384 = "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c6\
                      0c2ef6ab4030fe8296248df163f44952";
        let exp256 = "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54";
        let exp224 = "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e";
        check(exp512, exp384, exp256, exp224, &key, data);

        let data =
            b"This is a test using a larger than block-size key and a larger than block-\
              size data. The key needs to be hashed before being used by the HMAC algorithm.";
        let exp512 = "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944\
                      b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58";
        let exp384 = "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5\
                      a678cc31e799176d3860e6110c46523e";
        let exp256 = "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2";
        let exp224 = "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1";
        check(exp512, exp384, exp256, exp224, &key, data);
    }
}
<commit_msg>hmac: add basic documentation<commit_after>//! Module for creating HMAC digests.
use sha2::{HashFunction, MAX_DIGEST_SIZE, Sha224, Sha256, Sha384, Sha512};

const IPAD: u8 = 0x36;
const OPAD: u8 = 0x5c;

/// A function for creating HMAC digests given a hash function `H`.
///
/// # Examples
///
/// ```
/// use crypto_pure::hmac::Hmac;
/// use crypto_pure::sha2::{HashFunction, Sha512};
/// # let key = b"This should be generated securely.";
/// let mut digest = [0; Sha512::DIGEST_SIZE];
/// let mut hmac = Hmac::<Sha512>::new(key);
/// hmac.update(b"part one");
/// hmac.update(b"part two");
/// hmac.write_digest(&mut digest);
/// ```
pub struct Hmac<H> {
    inner_hash_function: H,
    outer_hash_function: H,
}

macro_rules! impl_wrapper { ($function:ident, $key:expr, $message:expr) => {{
    let mut digest = [0; $function::DIGEST_SIZE];
    let mut hmac = Hmac::<$function>::new($key);
    hmac.update($message);
    hmac.write_digest(&mut digest);
    digest
}}}

/// Wrapper for obtaining the HMAC-SHA-512 digest for a complete message.
pub fn hmac_sha512(key: &[u8], message: &[u8]) -> [u8; Sha512::DIGEST_SIZE] {
    impl_wrapper!(Sha512, key, message)
}

/// Wrapper for obtaining the HMAC-SHA-384 digest for a complete message.
pub fn hmac_sha384(key: &[u8], message: &[u8]) -> [u8; Sha384::DIGEST_SIZE] {
    impl_wrapper!(Sha384, key, message)
}

/// Wrapper for obtaining the HMAC-SHA-256 digest for a complete message.
pub fn hmac_sha256(key: &[u8], message: &[u8]) -> [u8; Sha256::DIGEST_SIZE] {
    impl_wrapper!(Sha256, key, message)
}

/// Wrapper for obtaining the HMAC-SHA-224 digest for a complete message.
pub fn hmac_sha224(key: &[u8], message: &[u8]) -> [u8; Sha224::DIGEST_SIZE] {
    impl_wrapper!(Sha224, key, message)
}

impl<H: HashFunction> Hmac<H> {
    /// Initializes an HMAC function given a key.
    pub fn new(key: &[u8]) -> Self {
        let mut hashed_key;
        let new_key;
        if key.len() > H::BLOCK_SIZE {
            hashed_key = [0; MAX_DIGEST_SIZE];
            let mut hash_function = H::default();
            hash_function.update(key);
            hash_function.write_digest(&mut hashed_key[..H::DIGEST_SIZE]);
            new_key = &hashed_key[..H::DIGEST_SIZE];
        } else {
            new_key = key;
        }
        Self {
            inner_hash_function: Self::keyed_hash_function(new_key, IPAD),
            outer_hash_function: Self::keyed_hash_function(new_key, OPAD),
        }
    }

    /// Feeds input into the function to update its state.
    pub fn update(&mut self, input: &[u8]) {
        self.inner_hash_function.update(input);
    }

    /// Writes the digest into an output buffer.
    ///
    /// # Panics
    ///
    /// Panics if `output.len()` is not equal to the digest size.
    pub fn write_digest(mut self, output: &mut [u8]) {
        assert_eq!(H::DIGEST_SIZE, output.len());
        self.inner_hash_function.write_digest(output);
        self.outer_hash_function.update(output);
        self.outer_hash_function.write_digest(output);
    }

    fn keyed_hash_function(key: &[u8], pad: u8) -> H {
        let mut hash_function = H::default();
        for byte in key {
            hash_function.update(&[byte ^ pad]);
        }
        for _ in key.len()..H::BLOCK_SIZE {
            hash_function.update(&[pad]);
        }
        hash_function
    }
}

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

    fn check(exp512: &str, exp384: &str, exp256: &str, exp224: &str, key: &[u8], data: &[u8]) {
        let expected = h2b(exp512);
        let mut actual = hmac_sha512(key, data);
        assert_eq!(expected, actual.to_vec());

        let mut hmac = Hmac::<Sha512>::new(key);
        for word in data.chunks(4) {
            hmac.update(word);
        }
        hmac.write_digest(&mut actual);
        assert_eq!(expected, actual.to_vec());

        let expected = h2b(exp384);
        let mut actual = hmac_sha384(key, data);
        assert_eq!(expected, actual.to_vec());

        let mut hmac = Hmac::<Sha384>::new(key);
        for word in data.chunks(4) {
            hmac.update(word);
        }
        hmac.write_digest(&mut actual);
        assert_eq!(expected, actual.to_vec());

        let expected = h2b(exp256);
        let mut actual = hmac_sha256(key, data);
        assert_eq!(expected, actual.to_vec());

        let mut hmac = Hmac::<Sha256>::new(key);
        for word in data.chunks(4) {
            hmac.update(word);
        }
        hmac.write_digest(&mut actual);
        assert_eq!(expected, actual.to_vec());

        let expected = h2b(exp224);
        let mut actual = hmac_sha224(key, data);
        assert_eq!(expected, actual.to_vec());

        let mut hmac = Hmac::<Sha224>::new(key);
        for word in data.chunks(4) {
            hmac.update(word);
        }
        hmac.write_digest(&mut actual);
        assert_eq!(expected, actual.to_vec());
    }

    #[test]
    fn test_digest() {
        let key = h2b("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
        let data = b"Hi There";
        let exp512 = "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\
                      daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854";
        let exp384 = "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59c\
                      faea9ea9076ede7f4af152e8b2fa9cb6";
        let exp256 = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7";
        let exp224 = "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22";
        check(exp512, exp384, exp256, exp224, &key, data);

        let key = b"Jefe";
        let data = b"what do ya want for nothing?";
        let exp512 = "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\
                      9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737";
        let exp384 = "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e\
                      8e2240ca5e69e2c78b3239ecfab21649";
        let exp256 = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843";
        let exp224 = "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44";
        check(exp512, exp384, exp256, exp224, key, data);

        let key = [0xaa; 20];
        let data = [0xdd; 50];
        let exp512 = "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39\
                      bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb";
        let exp384 = "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b\
                      2a5ab39dc13814b94e3ab6e101a34f27";
        let exp256 = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe";
        let exp224 = "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea";
        check(exp512, exp384, exp256, exp224, &key, &data);

        let key: Vec<_> = (0x01..0x1a).collect();
        let data = [0xcd; 50];
        let exp512 = "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db\
                      a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd";
        let exp384 = "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e\
                      6801dd23c4a7d679ccf8a386c674cffb";
        let exp256 = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b";
        let exp224 = "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a";
        check(exp512, exp384, exp256, exp224, &key, &data);

        let key = [0xaa; 131];
        let data = b"Test Using Larger Than Block-Size Key - Hash Key First";
        let exp512 = "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352\
                      6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598";
        let exp384 = "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c6\
                      0c2ef6ab4030fe8296248df163f44952";
        let exp256 = "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54";
        let exp224 = "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e";
        check(exp512, exp384, exp256, exp224, &key, data);

        let data =
            b"This is a test using a larger than block-size key and a larger than block-\
              size data. The key needs to be hashed before being used by the HMAC algorithm.";
        let exp512 = "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944\
                      b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58";
        let exp384 = "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5\
                      a678cc31e799176d3860e6110c46523e";
        let exp256 = "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2";
        let exp224 = "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1";
        check(exp512, exp384, exp256, exp224, &key, data);
    }
}
<|endoftext|>