<commit_before>// Copyright (c) 2017 FaultyRAM
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
// or http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! A helper library for use in the Google Code Jam.

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", forbid(clippy))]
#![cfg_attr(feature = "clippy", forbid(clippy_internal))]
#![cfg_attr(feature = "clippy", forbid(clippy_pedantic))]
#![cfg_attr(feature = "clippy", forbid(clippy_restrictions))]
#![forbid(warnings)]
#![forbid(box_pointers)]
#![forbid(fat_ptr_transmutes)]
#![forbid(missing_copy_implementations)]
#![forbid(missing_debug_implementations)]
#![forbid(missing_docs)]
#![forbid(trivial_casts)]
#![forbid(trivial_numeric_casts)]
#![forbid(unsafe_code)]
#![forbid(unused_extern_crates)]
#![forbid(unused_import_braces)]
#![deny(unused_qualifications)]
#![forbid(unused_results)]
#![forbid(variant_size_differences)]

use std::{env, io};
use std::fmt::Arguments;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, LineWriter, Lines, Write};
use std::ops::{Add, AddAssign};
use std::path::{Path, PathBuf};

/// A set of test cases loaded from an input file.
#[derive(Debug)]
pub struct TestCases {
    /// An iterator over the lines of an input file.
    input: Lines<BufReader<File>>,
    /// An output file.
    output: LineWriter<File>,
    /// The current test case.
    current_case: usize,
    /// The total number of test cases to handle.
    case_count: usize,
}

/// Supports reading lines from an input file and writing data to an output file.
#[derive(Debug)]
pub struct TestCaseIo<'a>(&'a mut TestCases);

impl TestCases {
    /// Obtains a set of test cases.
    pub fn new() -> TestCases {
        let arg = env::args().nth(1).expect("input file path not specified");
        let input_file_path: &Path = arg.as_ref();
        let output_file_path: PathBuf = input_file_path.with_extension("out");
        assert_ne!(input_file_path, output_file_path);
        let mut test_cases = TestCases {
            input: Self::open_input_file(input_file_path),
            output: Self::open_output_file(&output_file_path),
            current_case: 1,
            case_count: 0,
        };
        test_cases.init_test_case_count();
        test_cases
    }

    /// Consumes a set of test cases, calling a closure once for each test case.
    pub fn run<F: Fn(&mut TestCaseIo)>(mut self, f: F) {
        let mut tc_io = TestCaseIo::new(&mut self);
        while tc_io.0.current_case <= tc_io.0.case_count {
            let current_case_s = tc_io.0.current_case_string();
            let _ = tc_io.write(current_case_s.as_bytes())
                .expect("could not write current test case string to output file");
            (f)(&mut tc_io);
            tc_io.0.current_case.add_assign(1);
        }
    }

    /// Creates an iterator over the lines of an input file.
    fn open_input_file<P: AsRef<Path>>(path: P) -> Lines<BufReader<File>> {
        BufReader::new(OpenOptions::new()
            .read(true)
            .open(path)
            .expect("could not open input file for reading")).lines()
    }

    /// Creates an output file.
    fn open_output_file<P: AsRef<Path>>(path: P) -> LineWriter<File> {
        LineWriter::new(OpenOptions::new()
                            .write(true)
                            .truncate(true)
                            .create(true)
                            .open(path)
                            .expect("could not open output file for writing"))
    }

    /// Reads a line of text from the input file.
    fn read_line(&mut self) -> String {
        self.input
            .next()
            .expect("reached end of file")
            .expect("could not read from input file")
    }

    /// Obtains the number of test cases to examine from the input file.
    fn init_test_case_count(&mut self) {
        let line = self.read_line();
        self.case_count =
            usize::from_str_radix(&line, 10).expect("could not parse test case count");
    }

    /// Creates a string identifying the current test case.
    fn current_case_string(&mut self) -> String {
        let case_str = "Case #";
        let case_s = self.current_case.to_string();
        let case_str_colon = ":";
        let mut out = String::with_capacity(0);
        out.reserve_exact(case_str.len().add(case_s.len()).add(case_str_colon.len()));
        out.push_str(case_str);
        out.push_str(&case_s);
        out.push_str(case_str_colon);
        out
    }
}

impl Default for TestCases {
    fn default() -> TestCases {
        Self::new()
    }
}

impl<'a> TestCaseIo<'a> {
    /// Reads a line from the input file.
    pub fn read_line(&mut self) -> String {
        self.0.read_line()
    }

    /// Returns the current test case number.
    pub fn current_case(&self) -> usize {
        self.0.current_case
    }

    /// Returns the total number of test cases in the input file.
    pub fn case_count(&self) -> usize {
        self.0.case_count
    }

    /// Creates a new `TestCaseIo` instance.
    fn new(test_cases: &'a mut TestCases) -> TestCaseIo<'a> {
        TestCaseIo(test_cases)
    }
}

impl<'a> Write for TestCaseIo<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.output.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.output.flush()
    }

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        self.0.output.write_all(buf)
    }

    fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> {
        self.0.output.write_fmt(fmt)
    }
}
<commit_msg>Refactoring<commit_after>// Copyright (c) 2017 FaultyRAM
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
// or http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! A helper library for use in the Google Code Jam.

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", forbid(clippy))]
#![cfg_attr(feature = "clippy", forbid(clippy_internal))]
#![cfg_attr(feature = "clippy", forbid(clippy_pedantic))]
#![cfg_attr(feature = "clippy", forbid(clippy_restrictions))]
#![forbid(warnings)]
#![forbid(box_pointers)]
#![forbid(fat_ptr_transmutes)]
#![forbid(missing_copy_implementations)]
#![forbid(missing_debug_implementations)]
#![forbid(missing_docs)]
#![forbid(trivial_casts)]
#![forbid(trivial_numeric_casts)]
#![forbid(unsafe_code)]
#![forbid(unused_extern_crates)]
#![forbid(unused_import_braces)]
#![deny(unused_qualifications)]
#![forbid(unused_results)]
#![forbid(variant_size_differences)]

use std::{env, io};
use std::fmt::Arguments;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, LineWriter, Lines, Write};
use std::ops::{Add, AddAssign};
use std::path::Path;

/// A set of test cases loaded from an input file.
#[derive(Debug)]
pub struct TestCases {
    /// An iterator over the lines of an input file.
    input: Lines<BufReader<File>>,
    /// An output file.
    output: LineWriter<File>,
    /// The current test case.
    current_case: usize,
    /// The total number of test cases to handle.
    case_count: usize,
}

/// Supports reading lines from an input file and writing data to an output file.
#[derive(Debug)]
pub struct TestCaseIo(TestCases);

impl TestCases {
    /// Obtains a set of test cases.
    pub fn new() -> TestCases {
        let arg = env::args().nth(1).expect("input file path not specified");
        let input_file_path = Path::new(&arg);
        let output_file_path = input_file_path.with_extension("out");
        assert_ne!(input_file_path, output_file_path);
        let mut test_cases = TestCases {
            input: Self::open_input_file(input_file_path),
            output: Self::open_output_file(&output_file_path),
            current_case: 1,
            case_count: 0,
        };
        test_cases.init_test_case_count();
        test_cases
    }

    /// Consumes a set of test cases, calling a closure once for each test case.
    pub fn run<F: Fn(&mut TestCaseIo)>(self, f: F) {
        let mut tc_io = TestCaseIo::new(self);
        while tc_io.0.current_case <= tc_io.0.case_count {
            let current_case_s = tc_io.0.current_case_string();
            let _ = tc_io.write(current_case_s.as_bytes())
                .expect("could not write current test case string to output file");
            (f)(&mut tc_io);
            tc_io.0.current_case.add_assign(1);
        }
    }

    /// Creates an iterator over the lines of an input file.
    fn open_input_file<P: AsRef<Path>>(path: P) -> Lines<BufReader<File>> {
        BufReader::new(OpenOptions::new()
            .read(true)
            .open(path)
            .expect("could not open input file for reading")).lines()
    }

    /// Creates an output file.
    fn open_output_file<P: AsRef<Path>>(path: P) -> LineWriter<File> {
        LineWriter::new(OpenOptions::new()
                            .write(true)
                            .truncate(true)
                            .create(true)
                            .open(path)
                            .expect("could not open output file for writing"))
    }

    /// Reads a line of text from the input file.
    fn read_line(&mut self) -> String {
        self.input
            .next()
            .expect("reached end of file")
            .expect("could not read from input file")
    }

    /// Obtains the number of test cases to examine from the input file.
    fn init_test_case_count(&mut self) {
        let line = self.read_line();
        self.case_count =
            usize::from_str_radix(&line, 10).expect("could not parse test case count");
    }

    /// Creates a string identifying the current test case.
    fn current_case_string(&mut self) -> String {
        let case_str = "Case #";
        let case_s = self.current_case.to_string();
        let case_str_colon = ":";
        let mut out = String::with_capacity(0);
        out.reserve_exact(case_str.len().add(case_s.len()).add(case_str_colon.len()));
        out.push_str(case_str);
        out.push_str(&case_s);
        out.push_str(case_str_colon);
        out
    }
}

impl Default for TestCases {
    fn default() -> TestCases {
        Self::new()
    }
}

impl TestCaseIo {
    /// Reads a line from the input file.
    pub fn read_line(&mut self) -> String {
        self.0.read_line()
    }

    /// Returns the current test case number.
    pub fn current_case(&self) -> usize {
        self.0.current_case
    }

    /// Returns the total number of test cases in the input file.
    pub fn case_count(&self) -> usize {
        self.0.case_count
    }

    /// Creates a new `TestCaseIo` instance.
    fn new(test_cases: TestCases) -> TestCaseIo {
        TestCaseIo(test_cases)
    }
}

impl Write for TestCaseIo {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.output.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.output.flush()
    }

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        self.0.output.write_all(buf)
    }

    fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> {
        self.0.output.write_fmt(fmt)
    }
}
<|endoftext|>


<commit_before>//! Library for manipulating Game Maker Studio's "data.win" (GEN8) data files.
//!
//! Typical usage consists of reading [`GameData`](struct.GameData.html) from a
//! file, doing stuff with it,
//! then writing it back to a file.
//!
//! Example:
//!
//! ```no_run
//! let mut game_data = rgmk::GameData::from_file("data.win")
//!                                    .expect("Failed to open data.win");
//! game_data.strings.strings[0] = "NYEH HEH HEH!".into();
//! game_data.save_to_file("data.win").expect("Failed to save data.win");
//! ```
//!

#![feature(associated_consts, pub_restricted)]
#![warn(missing_docs, trivial_casts, trivial_numeric_casts)]

#[macro_use]
extern crate quick_error;
extern crate byteorder;
#[macro_use]
extern crate log;

mod gamedata_io;

pub use gamedata_io::{ReadError, StringReadError};

use std::io::{self, Read, Write};
use std::path;

/// The data of a Game Maker Studio game.
///
/// This is the collective information acquired from "data.win".
pub struct GameData {
    metadata: MetaData,
    options: Options,
    extn: Extn,
    /// The sounds of the game.
    pub sounds: Sounds,
    audio_groups: Option<AudioGroups>,
    /// The sprites of the game.
    pub sprites: Sprites,
    backgrounds: Backgrounds,
    paths: Paths,
    scripts: Scripts,
    shaders: Shaders,
    /// The fonts of the game.
    pub fonts: Fonts,
    timelines: Timelines,
    objects: Objects,
    rooms: Rooms,
    dafl: Dafl,
    tpag: Tpag,
    /// The code data of the game.
    pub code: Code,
    variables: Variables,
    functions: Functions,
    /// The strings of the game.
    pub strings: Strings,
    /// The texture data of the game.
    pub textures: Textures,
    audio: Audio,
}

/// A reader that satisfies the requirements for reading a `GameData`.
pub trait GameDataRead: Read + io::Seek {}
impl<T: Read + io::Seek> GameDataRead for T {}
/// A writer that satisfies the requirements for writing a `GameData`.
pub trait GameDataWrite: Write + io::Seek {}
impl<T: Write + io::Seek> GameDataWrite for T {}

impl GameData {
    /// Reads a GameData from a reader.
    pub fn from_reader<R: GameDataRead>(reader: &mut R) -> Result<GameData, ReadError> {
        gamedata_io::read(reader)
    }
    /// Reads a GameData from a file.
    pub fn from_file<P: AsRef<path::Path>>(path: P) -> Result<GameData, ReadError> {
        use std::fs::File;
        use std::io::BufReader;
        let file = File::open(path)?;
        GameData::from_reader(&mut BufReader::new(file))
    }
    /// Writes self to a writer.
    pub fn write_to_writer<W: GameDataWrite>(&self, writer: &mut W) -> io::Result<()> {
        gamedata_io::write(self, writer)
    }
    /// Writes self to a file.
    pub fn save_to_file<P: AsRef<path::Path>>(&self, path: P) -> io::Result<()> {
        use std::fs::File;
        use std::io::BufWriter;
        let file = File::create(path)?;
        self.write_to_writer(&mut BufWriter::new(file))
    }
    /// Returns the window width of the game.
    pub fn window_width(&self) -> u32 {
        self.metadata.window_width
    }
    /// Returns the window height of the game.
    pub fn window_height(&self) -> u32 {
        self.metadata.window_height
    }
    /// Sets the window dimensions of the game.
    pub fn set_window_dimensions(&mut self, width: u32, height: u32) {
        self.metadata.window_width = width;
        self.metadata.window_height = height;
    }
    /// Returns the window title of the game.
    pub fn window_title(&self) -> &str {
        &self.strings.strings[self.metadata.window_title_index]
    }
    /// Sets the window title of the game.
    pub fn set_window_title<S: Into<String>>(&mut self, new: S) {
        self.strings.strings[self.metadata.window_title_index] = new.into();
    }
}

/// Contains various metadata, for example, the window width/height/title.
struct MetaData {
    // Possibly some kind of GEN8 version number. Unsure.
    // But let's assume, since we need to take multiple versions into consideration, and
    // this value seems to differ for different versions.
    possibly_gen8_version: u32,
    game_id_1_index: usize, // Some kind of game id
    default_index: usize, // Points to "Default"
    unk2: u32,
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
    unk7: u32,
    unk8: u32,
    game_id_2_index: usize, // Some kind of game id, identical to game_id_1
    unk9: u32,
    unk10: u32,
    unk11: u32,
    unk12: u32,
    window_width: u32,
    window_height: u32,
    unk13: u32,
    unk14: u32,
    unk15: u32,
    unk16: u32,
    unk17: u32,
    unk18: u32,
    unk19: u32,
    unk20: u32,
    window_title_index: usize,
    unknown: Vec<u32>,
}

/// Game Maker project Options
struct Options {
    unk1: u32, // Unknown
    unk2: u32, // Unknown
    icon_offset: u32, // Points to texture data (icon?)
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
    unk7: u32,
    unk8: u32,
    unk9: u32,
    unk10: u32,
    unk11: u32,
    unk12: u32,
    unk13: u32,
    unk14: u32,
    unk15: u32,
    constant1_name_index: usize, // Offset of the name of this constant in the string table
    constant2_name_index: usize, // Offset of the name of this constant in the string table
    constant3_name_index: usize, // Offset of the name of this constant in the string table
    constant4_name_index: usize, // Offset of the name of this constant in the string table
    constant5_name_index: usize, // Offset of the name of this constant in the string table
    constant6_name_index: usize, // Offset of the name of this constant in the string table
    constant7_name_index: usize, // Offset of the name of this constant in the string table
    constant8_name_index: usize, // Offset of the name of this constant in the string table
    constant9_name_index: usize, // Offset of the name of this constant in the string table
    constant10_name_index: usize, // Offset of the name of this constant in the string table
    constant11_name_index: usize, // Offset of the name of this constant in the string table
    constant12_name_index: usize, // Offset of the name of this constant in the string table
    constant13_name_index: usize, // Offset of the name of this constant in the string table
    constant14_name_index: usize, // Offset of the name of this constant in the string table
}

/// Purpose unknown.
struct Extn {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker sound.
#[derive(Clone, Copy)]
pub struct Sound {
    /// Index of the name in the string table.
    pub name_index: usize,
    unk1: u32,
    /// Index of the extension in the string table.
    pub ext_index: usize,
    /// Index of the filename in the string table.
    pub filename_index: usize,
    unk2: u32,
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
}

/// A collection of sounds.
pub struct Sounds {
    /// The sounds.
    pub sounds: Vec<Sound>,
}

/// Collection of audio groups. Not present in all games.
struct AudioGroups {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker sprite.
#[derive(Clone)]
pub struct Sprite {
    /// The index of the resource name in the string table.
    pub name_index: usize,
    /// The width of the sprite
    pub width: u32,
    /// The height of the sprite
    pub height: u32,
    unknown: Vec<u8>,
}

/// A collection of sprites.
pub struct Sprites {
    /// The sprites
    pub sprites: Vec<Sprite>,
}

/// A collection of backgrounds.
struct Backgrounds {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of paths.
struct Paths {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker script.
struct Script {
    /// Index of the name of the script in the string table
    pub name_index: usize,
    unknown: u32, // Unknown
}

/// A collection of scripts.
struct Scripts {
    /// The scripts.
    pub scripts: Vec<Script>,
}

/// A collection of shaders.
struct Shaders {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A font.
#[derive(Clone)]
pub struct Font {
    /// Index of the font's resource name in the string table.
    pub name_index: usize,
    /// Index of the font's font name in the string table.
    pub font_name_index: usize,
    /// Point size of the font.
    pub point_size: u32,
    data: Vec<u8>,
}

/// A collection of fonts.
pub struct Fonts {
    /// The fonts.
    pub fonts: Vec<Font>,
    unknown: Vec<u8>, // Unknown trailing data
}

/// A collection of timelines.
struct Timelines {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of objects.
struct Objects {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of rooms.
struct Rooms {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Purpose unknown.
struct Dafl {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Purpose unknown.
struct Tpag {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A chunk of code
pub struct CodeChunk {
    /// Index of the name of the code chunk in the string table.
    pub name_index: usize,
    /// The raw code.
    pub raw_code: Vec<u8>,
}

/// A collection of code chunks.
pub struct Code {
    /// The actual code chunks.
    pub code_chunks: Vec<CodeChunk>,
}

/// A game maker variable.
struct Variable {
    /// Index of the name of the variable in the strings section.
    pub name_index: usize,
    unknown: u32, // Purpose unknown. Ranges from 1 to a few thousand.
    code_offset: u32, // Points into the code section
}

/// A collection of variables.
struct Variables {
    /// The variables.
    pub variables: Vec<Variable>,
}

/// A game maker function.
struct Function {
    /// Index of the name of the function in the strings section.
    pub name_index: usize,
    unknown: u32, // Purpose unknown. Ranges from 1 to a few thousand.
    code_offset: u32, // Points into the code section.
}

/// A collection of functions.
struct Functions {
    /// The functions.
    pub functions: Vec<Function>,
}

/// A collection of strings.
///
/// All strings are assumed to be valid UTF-8.
pub struct Strings {
    /// The vector holding the strings.
    pub strings: Vec<String>,
}

/// A texture.
///
/// Currently only PNG textures are supported.
pub struct Texture {
    unknown: u32, // Purpose unknown. Always seems to be 1.
    /// The PNG data associated with this texture.
    pub png_data: Vec<u8>,
}

/// A collection of textures.
pub struct Textures {
    /// The textures.
    pub textures: Vec<Texture>,
}

/// Raw audio data.
struct AudioData {
    data: Vec<u8>,
}

/// A collection of audio data.
struct Audio {
    audio: Vec<AudioData>,
    offsets: Vec<u32>, // Audio data is not contiguous, so we need to store relative offsets
}
<commit_msg>Don't need to feature gate pub(restricted) anymore<commit_after>//! Library for manipulating Game Maker Studio's "data.win" (GEN8) data files.
//!
//! Typical usage consists of reading [`GameData`](struct.GameData.html) from a
//! file, doing stuff with it,
//! then writing it back to a file.
//!
//! Example:
//!
//! ```no_run
//! let mut game_data = rgmk::GameData::from_file("data.win")
//!                                    .expect("Failed to open data.win");
//! game_data.strings.strings[0] = "NYEH HEH HEH!".into();
//! game_data.save_to_file("data.win").expect("Failed to save data.win");
//! ```
//!

#![feature(associated_consts)]
#![warn(missing_docs, trivial_casts, trivial_numeric_casts)]

#[macro_use]
extern crate quick_error;
extern crate byteorder;
#[macro_use]
extern crate log;

mod gamedata_io;

pub use gamedata_io::{ReadError, StringReadError};

use std::io::{self, Read, Write};
use std::path;

/// The data of a Game Maker Studio game.
///
/// This is the collective information acquired from "data.win".
pub struct GameData {
    metadata: MetaData,
    options: Options,
    extn: Extn,
    /// The sounds of the game.
    pub sounds: Sounds,
    audio_groups: Option<AudioGroups>,
    /// The sprites of the game.
    pub sprites: Sprites,
    backgrounds: Backgrounds,
    paths: Paths,
    scripts: Scripts,
    shaders: Shaders,
    /// The fonts of the game.
    pub fonts: Fonts,
    timelines: Timelines,
    objects: Objects,
    rooms: Rooms,
    dafl: Dafl,
    tpag: Tpag,
    /// The code data of the game.
    pub code: Code,
    variables: Variables,
    functions: Functions,
    /// The strings of the game.
    pub strings: Strings,
    /// The texture data of the game.
    pub textures: Textures,
    audio: Audio,
}

/// A reader that satisfies the requirements for reading a `GameData`.
pub trait GameDataRead: Read + io::Seek {}
impl<T: Read + io::Seek> GameDataRead for T {}
/// A writer that satisfies the requirements for writing a `GameData`.
pub trait GameDataWrite: Write + io::Seek {}
impl<T: Write + io::Seek> GameDataWrite for T {}

impl GameData {
    /// Reads a GameData from a reader.
    pub fn from_reader<R: GameDataRead>(reader: &mut R) -> Result<GameData, ReadError> {
        gamedata_io::read(reader)
    }
    /// Reads a GameData from a file.
    pub fn from_file<P: AsRef<path::Path>>(path: P) -> Result<GameData, ReadError> {
        use std::fs::File;
        use std::io::BufReader;
        let file = File::open(path)?;
        GameData::from_reader(&mut BufReader::new(file))
    }
    /// Writes self to a writer.
    pub fn write_to_writer<W: GameDataWrite>(&self, writer: &mut W) -> io::Result<()> {
        gamedata_io::write(self, writer)
    }
    /// Writes self to a file.
    pub fn save_to_file<P: AsRef<path::Path>>(&self, path: P) -> io::Result<()> {
        use std::fs::File;
        use std::io::BufWriter;
        let file = File::create(path)?;
        self.write_to_writer(&mut BufWriter::new(file))
    }
    /// Returns the window width of the game.
    pub fn window_width(&self) -> u32 {
        self.metadata.window_width
    }
    /// Returns the window height of the game.
    pub fn window_height(&self) -> u32 {
        self.metadata.window_height
    }
    /// Sets the window dimensions of the game.
    pub fn set_window_dimensions(&mut self, width: u32, height: u32) {
        self.metadata.window_width = width;
        self.metadata.window_height = height;
    }
    /// Returns the window title of the game.
    pub fn window_title(&self) -> &str {
        &self.strings.strings[self.metadata.window_title_index]
    }
    /// Sets the window title of the game.
    pub fn set_window_title<S: Into<String>>(&mut self, new: S) {
        self.strings.strings[self.metadata.window_title_index] = new.into();
    }
}

/// Contains various metadata, for example, the window width/height/title.
struct MetaData {
    // Possibly some kind of GEN8 version number. Unsure.
    // But let's assume, since we need to take multiple versions into consideration, and
    // this value seems to differ for different versions.
    possibly_gen8_version: u32,
    game_id_1_index: usize, // Some kind of game id
    default_index: usize, // Points to "Default"
    unk2: u32,
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
    unk7: u32,
    unk8: u32,
    game_id_2_index: usize, // Some kind of game id, identical to game_id_1
    unk9: u32,
    unk10: u32,
    unk11: u32,
    unk12: u32,
    window_width: u32,
    window_height: u32,
    unk13: u32,
    unk14: u32,
    unk15: u32,
    unk16: u32,
    unk17: u32,
    unk18: u32,
    unk19: u32,
    unk20: u32,
    window_title_index: usize,
    unknown: Vec<u32>,
}

/// Game Maker project Options
struct Options {
    unk1: u32, // Unknown
    unk2: u32, // Unknown
    icon_offset: u32, // Points to texture data (icon?)
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
    unk7: u32,
    unk8: u32,
    unk9: u32,
    unk10: u32,
    unk11: u32,
    unk12: u32,
    unk13: u32,
    unk14: u32,
    unk15: u32,
    constant1_name_index: usize, // Offset of the name of this constant in the string table
    constant2_name_index: usize, // Offset of the name of this constant in the string table
    constant3_name_index: usize, // Offset of the name of this constant in the string table
    constant4_name_index: usize, // Offset of the name of this constant in the string table
    constant5_name_index: usize, // Offset of the name of this constant in the string table
    constant6_name_index: usize, // Offset of the name of this constant in the string table
    constant7_name_index: usize, // Offset of the name of this constant in the string table
    constant8_name_index: usize, // Offset of the name of this constant in the string table
    constant9_name_index: usize, // Offset of the name of this constant in the string table
    constant10_name_index: usize, // Offset of the name of this constant in the string table
    constant11_name_index: usize, // Offset of the name of this constant in the string table
    constant12_name_index: usize, // Offset of the name of this constant in the string table
    constant13_name_index: usize, // Offset of the name of this constant in the string table
    constant14_name_index: usize, // Offset of the name of this constant in the string table
}

/// Purpose unknown.
struct Extn {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker sound.
#[derive(Clone, Copy)]
pub struct Sound {
    /// Index of the name in the string table.
    pub name_index: usize,
    unk1: u32,
    /// Index of the extension in the string table.
    pub ext_index: usize,
    /// Index of the filename in the string table.
    pub filename_index: usize,
    unk2: u32,
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
}

/// A collection of sounds.
pub struct Sounds {
    /// The sounds.
    pub sounds: Vec<Sound>,
}

/// Collection of audio groups. Not present in all games.
struct AudioGroups {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker sprite.
#[derive(Clone)]
pub struct Sprite {
    /// The index of the resource name in the string table.
    pub name_index: usize,
    /// The width of the sprite
    pub width: u32,
    /// The height of the sprite
    pub height: u32,
    unknown: Vec<u8>,
}

/// A collection of sprites.
pub struct Sprites {
    /// The sprites
    pub sprites: Vec<Sprite>,
}

/// A collection of backgrounds.
struct Backgrounds {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of paths.
struct Paths {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker script.
struct Script {
    /// Index of the name of the script in the string table
    pub name_index: usize,
    unknown: u32, // Unknown
}

/// A collection of scripts.
struct Scripts {
    /// The scripts.
    pub scripts: Vec<Script>,
}

/// A collection of shaders.
struct Shaders {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A font.
#[derive(Clone)]
pub struct Font {
    /// Index of the font's resource name in the string table.
    pub name_index: usize,
    /// Index of the font's font name in the string table.
    pub font_name_index: usize,
    /// Point size of the font.
    pub point_size: u32,
    data: Vec<u8>,
}

/// A collection of fonts.
pub struct Fonts {
    /// The fonts.
    pub fonts: Vec<Font>,
    unknown: Vec<u8>, // Unknown trailing data
}

/// A collection of timelines.
struct Timelines {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of objects.
struct Objects {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of rooms.
struct Rooms {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Purpose unknown.
struct Dafl {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Purpose unknown.
struct Tpag {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A chunk of code
pub struct CodeChunk {
    /// Index of the name of the code chunk in the string table.
    pub name_index: usize,
    /// The raw code.
    pub raw_code: Vec<u8>,
}

/// A collection of code chunks.
pub struct Code {
    /// The actual code chunks.
    pub code_chunks: Vec<CodeChunk>,
}

/// A game maker variable.
struct Variable {
    /// Index of the name of the variable in the strings section.
    pub name_index: usize,
    unknown: u32, // Purpose unknown. Ranges from 1 to a few thousand.
    code_offset: u32, // Points into the code section
}

/// A collection of variables.
struct Variables {
    /// The variables.
    pub variables: Vec<Variable>,
}

/// A game maker function.
struct Function {
    /// Index of the name of the function in the strings section.
    pub name_index: usize,
    unknown: u32, // Purpose unknown. Ranges from 1 to a few thousand.
    code_offset: u32, // Points into the code section.
}

/// A collection of functions.
struct Functions {
    /// The functions.
    pub functions: Vec<Function>,
}

/// A collection of strings.
///
/// All strings are assumed to be valid UTF-8.
pub struct Strings {
    /// The vector holding the strings.
    pub strings: Vec<String>,
}

/// A texture.
///
/// Currently only PNG textures are supported.
pub struct Texture {
    unknown: u32, // Purpose unknown. Always seems to be 1.
    /// The PNG data associated with this texture.
    pub png_data: Vec<u8>,
}

/// A collection of textures.
pub struct Textures {
    /// The textures.
    pub textures: Vec<Texture>,
}

/// Raw audio data.
struct AudioData {
    data: Vec<u8>,
}

/// A collection of audio data.
struct Audio {
    audio: Vec<AudioData>,
    offsets: Vec<u32>, // Audio data is not contiguous, so we need to store relative offsets
}
<|endoftext|>


<commit_before>extern crate rust_base58;
extern crate openssl;
#[cfg(test)] extern crate rustc_serialize;

use self::HashType::*;

use openssl::crypto::hash as openssl_hash;
use rust_base58::ToBase58;
use std::hash::Hash;

// https://github.com/jbenet/multihash
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum HashType {
    SHA1 = 0x11,
    SHA2_256 = 0x12,
    SHA2_512 = 0x13,
    SHA3 = 0x14,
    BLAKE2B = 0x40,
    BLAKE2S = 0x41,
}

impl HashType {
    pub fn from_u8(x: u8) -> Option<HashType> {
        match x {
            0x11 => Some(SHA1),
            0x12 => Some(SHA2_256),
            0x13 => Some(SHA2_512),
            0x14 => Some(SHA3),
            0x40 => Some(BLAKE2B),
            0x41 => Some(BLAKE2S),
            _ => None,
        }
    }

    pub fn to_u8(&self) -> u8 {
        *self as u8
    }
}

// Takes bytes and a HashType, returns multihash of the bytes
pub fn multihash<'a>(data: &'a[u8], hash_type: HashType) -> Multihash {
    let openssl_type: openssl_hash::Type = match hash_type {
        SHA1 => openssl_hash::Type::SHA1,
        SHA2_256 => openssl_hash::Type::SHA256,
        SHA2_512 => openssl_hash::Type::SHA512,
        _ => panic!("That hash function is not yet implemented. Sorry"),
    };

    let hashed = openssl_hash::hash(openssl_type, data);

    Multihash::encode(&hashed[..], hash_type).unwrap()
}

struct HashFnTypeData {
    name: &'static str,
    default_len: u8,
}

fn hashfn_data(hft: &HashType) -> HashFnTypeData {
    match *hft {
        SHA1 =>
            HashFnTypeData { name: "sha1", default_len: 20 },
        SHA2_256 =>
            HashFnTypeData { name: "sha2-256", default_len: 32 },
        SHA2_512 =>
            HashFnTypeData { name: "sha2-512", default_len: 64 },
        SHA3 =>
            HashFnTypeData { name: "sha3", default_len: 64 },
        BLAKE2B =>
            HashFnTypeData { name: "blake2b", default_len: 64 },
        BLAKE2S =>
            HashFnTypeData { name: "blake2s", default_len: 32 },
    }
}

#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Multihash(Vec<u8>);

impl Multihash {
    pub fn new() -> Multihash {
        Multihash(Vec::new())
    }

    pub fn as_bytes(&self) -> &[u8] {
        &self.0[..]
    }

    // Constructs a Multihash out of the bytes of a hash and the
    // HashFnCode corresponding to the type of hash function used
    pub fn encode<'a>(digest: &'a [u8], hash_type: HashType) -> Result<Multihash, String> {
        let size = digest.len();
        if size > 127 {
            return Err("Digest length > 127 is currently not supported.".to_string())
        }

        let mut v = Vec::with_capacity(size + 2);
        v.push(hash_type.to_u8());
        v.push(size as u8);
        v.extend(digest);
        Ok(Multihash(v))
    }

    pub fn decode<'a>(&'a self) -> Result<DecodedMultiHash<'a>, DecodeError> {
        let mhlen = self.0.len();
        if mhlen < 3 {
            return Err(DecodeError::TooShort);
        } else if mhlen > 129 {
            return Err(DecodeError::TooLong);
        } else {
            let digest_len = (mhlen - 2) as u8;
            if digest_len != self.0[1] {
                return Err(DecodeError::InvalidDigestLength(self.0[1], digest_len));
            }
        }

        let fn_code = match HashType::from_u8(self.0[0]) {
            None => return Err(DecodeError::UnknownCode(self.0[0])),
            Some(code) => code,
        };

        let hash_fn = hashfn_data(&fn_code);

        let decoded = DecodedMultiHash {
            code: fn_code,
            name: hash_fn.name,
            length: self.0[1],
            digest: &self.0[2..],
        };
        Ok(decoded)

    }

    pub fn to_base58_string(&self) -> String {
        self.0.to_base58()
    }
}

pub struct DecodedMultiHash<'a> {
    code: HashType,
    name: &'static str,
    length: u8,
    digest: &'a [u8],
}

#[derive(Debug)]
pub enum DecodeError {
    UnknownCode(u8),
    TooShort,
    TooLong,
    InvalidDigestLength(u8, u8), // (stated, actual)
}

#[cfg(test)]
mod tests {
    use super::{multihash, Multihash, HashType, DecodedMultiHash, hashfn_data};
    use rustc_serialize::hex::FromHex;

    struct TestCase {
        hexstr: &'static str,
        hash_type: HashType,
    }

    impl TestCase {
        fn new(s: &'static str, ty: HashType) -> TestCase {
            TestCase { hexstr: s, hash_type: ty }
        }
    }

    static SHA1_STR: &'static str = "a228821137dacdbcd3ba5fa264f918fd
                                     a6223f7b";

    static SHA256_STR: &'static str = "95071c8e1ad3c7a016b30fd25853c1d9\
                                       e346ee7ee4afc977f554b139f71f4f30";

    static SHA512_STR: &'static str = "3912d4c1777934091ed95a15278b351e\
                                       00efa51f524af9af3320f0f6a227d551\
                                       76b1399684d15a05f03d1e519a9a9aa5\
                                       2a812a6d9bc63e5b485d6fd7ebf72114";

    #[test]
    fn test_multihash() {
        let abc = b"ABC";
        let sha1_hex = "3c01bdbb26f358bab27f267924aa2c9a03fcfdb8";
        let sha256_hex = "b5d4045c3f466fa91fe2cc6abe79232a1a57cdf104f7a26e716e0a1e2789df78";
        let sha512_hex = "397118fdac8d83ad98813c50759c85b8c47565d8268bf10da483153b747a7474\
                          3a58a90e85aa9f705ce6984ffc128db567489817e4092d050d8a1cc596ddc119";

        let sha1 = multihash(abc, HashType::SHA1);
        let mut sha1_bytes = hexstr_to_vec(sha1_hex);
        let mut expected_bytes = vec![0x11, 20];
        expected_bytes.append(&mut sha1_bytes);
        assert_eq!(sha1.as_bytes(), &expected_bytes[..]);

        let sha256 = multihash(abc, HashType::SHA2_256);
        let mut sha256_bytes = hexstr_to_vec(sha256_hex);
        let mut expected_bytes = vec![0x12, 32];
        expected_bytes.append(&mut sha256_bytes);
        assert_eq!(sha256.as_bytes(), &expected_bytes[..]);

        let sha512 = multihash(abc, HashType::SHA2_512);
        let mut sha512_bytes = hexstr_to_vec(sha512_hex);
        let mut expected_bytes = vec![0x13, 64];
        expected_bytes.append(&mut sha512_bytes);
        assert_eq!(sha512.as_bytes(), &expected_bytes[..]);
    }

    fn hexstr_to_vec(hexstr: &'static str) -> Vec<u8> {
        match hexstr.from_hex() {
            Err(e) => panic!("from_hex() failed with error: {:?}", e),
            Ok(vec) => vec,
        }
    }

    #[test]
    fn test_encode() {
        let mut cases = Vec::new();

        cases.push(TestCase::new(SHA1_STR, HashType::SHA1));
        cases.push(TestCase::new(SHA256_STR, HashType::SHA2_256));
        cases.push(TestCase::new(SHA512_STR, HashType::SHA2_512));

        for case in cases {
            let v = hexstr_to_vec(case.hexstr);

            let mut expected = Vec::new();
            expected.push(case.hash_type.to_u8());
            expected.push(v.len() as u8);
            expected.extend(&v[..]);

            let mh = Multihash::encode(&v[..], case.hash_type).unwrap();
            assert_eq!(&expected[..], mh.as_bytes());
        }
    }

    #[test]
    fn test_decode() {
        let mut cases = Vec::new();

        cases.push(TestCase::new(SHA1_STR, HashType::SHA1));
        cases.push(TestCase::new(SHA256_STR, HashType::SHA2_256));
        cases.push(TestCase::new(SHA512_STR, HashType::SHA2_512));

        for case in cases {
            let v = hexstr_to_vec(case.hexstr);
            let digest_length = v.len() as u8;
            let mh = Multihash::encode(&v[..], case.hash_type).unwrap();
            let hash_name = hashfn_data(&case.hash_type).name;

            match mh.decode() {
                Err(e) => panic!("Error decoding: {:?}", e),
                Ok(decoded) => {
                    assert_eq!(decoded.code, case.hash_type);
                    assert_eq!(decoded.name, hash_name);
                    assert_eq!(decoded.length, digest_length);
                    assert_eq!(decoded.digest, &v[..]);
                },
            }
        }
    }

}
<commit_msg>Misc. changes.<commit_after>extern crate rust_base58;
extern crate openssl;
#[cfg(test)] extern crate rustc_serialize;

use self::HashType::*;

use openssl::crypto::hash as openssl_hash;
use rust_base58::{FromBase58, ToBase58};

#[derive(Copy, Clone)]
pub enum HashType {
    SHA1 = 0x11,
    SHA2_256 = 0x12,
    SHA2_512 = 0x13,
    SHA3 = 0x14,
    BLAKE2B = 0x40,
    BLAKE2S = 0x41,
}

impl HashType {
    pub fn from_code(code: u8) -> Option<HashType> {
        match code {
            0x11 => Some(SHA1),
            0x12 => Some(SHA2_256),
            0x13 => Some(SHA2_512),
            0x14 => Some(SHA3),
            0x40 => Some(BLAKE2B),
            0x41 => Some(BLAKE2S),
            _ => None,
        }
    }

    fn digest_len(&self) -> u8 {
        match *self {
            SHA1     => 20,
            SHA2_256 => 32,
            SHA2_512 => 64,
            SHA3     => 64,
            BLAKE2B  => 64,
            BLAKE2S  => 32,
        }
    }

    fn code(&self) -> u8 {
        *self as u8
    }

    fn to_str(&self) -> &'static str {
        match *self {
            SHA1     => "sha1",
            SHA2_256 => "sha2-256",
            SHA2_512 => "sha2-512",
            SHA3     => "sha3",
            BLAKE2B  => "blake2b",
            BLAKE2S  => "blake2s",
        }
    }
}

// Takes bytes and a HashType, returns multihash of the bytes
pub fn multihash<'a>(data: &'a[u8], hash_type: HashType) -> Multihash {
    let openssl_type: openssl_hash::Type = match hash_type {
        SHA1     => openssl_hash::Type::SHA1,
        SHA2_256 => openssl_hash::Type::SHA256,
        SHA2_512 => openssl_hash::Type::SHA512,
        _ => panic!("That hash function is not yet implemented. Sorry"),
    };

    let hashed = openssl_hash::hash(openssl_type, data);

    Multihash::encode(&hashed[..], hash_type.code()).unwrap()
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Multihash {
    bytes: Vec<u8>
}

impl Multihash {
    /// Construct a multihash from a base58 string by parsing it into bytes. Verifies
    /// that the bytes are valid according to the Multihash format.
    pub fn from_base58_str(s: &str) -> Result<Multihash, String> {
        let bytes = try!(s.from_base58()
                          .map_err(|e| format!("Error parsing base58 string: {}", e)));

        let hash_code = bytes[0];

        if !is_valid_code(hash_code) {
            return Err(format!("Invalid hash function code: {}", hash_code));
        }

        if !is_app_code(hash_code) {
            let hash_type = HashType::from_code(hash_code).unwrap();
            let expected_digest_len = hash_type.digest_len();
            if expected_digest_len != bytes[1] {
                return Err(format!("For hash function {}, expected digest length {}, but \
                                    found digest length {}", hash_type.to_str(),
                                    expected_digest_len, bytes[1]));
            }
        }

        if (bytes.len() - 2) as u8 != bytes[1] {
            return Err(format!("Hash digest is {} bytes, which does not match the stated \
                                digest length {}", bytes.len() - 2, bytes[1]));
        }

        Ok(Multihash::from_vec(bytes))
    }

    /// Create a Multihash directly from a vector of bytes. Does not verify
    /// that the bytes are a valid Multihash.
    pub fn from_vec(vec: Vec<u8>) -> Multihash {
        Multihash { bytes: vec }
    }

    pub fn as_bytes(&self) -> &[u8] {
        &self.bytes[..]
    }

    /// Construct a Multihash out of the hash digest and hash function code
    pub fn encode<'a>(digest: &'a [u8], hash_code: u8) -> Result<Multihash, String> {
        let size = digest.len();
        if size > 127 {
            return Err("Digest length > 127 is currently not supported.".to_string())
        }

        if !is_valid_code(hash_code) {
            return Err(format!("Hash code {} is not recognized", hash_code));
        }

        let mut v = Vec::with_capacity(size + 2);
        v.push(hash_code);
        v.push(size as u8);
        v.extend(digest);
        Ok(Multihash::from_vec(v))
    }

    // TODO: evaluate whether this function makes sense
    /*
    pub fn decode<'a>(&'a self) -> Result<DecodedMultiHash<'a>, DecodeError> {
        let mhlen = self.bytes.len();
        if mhlen < 3 {
            return Err(DecodeError::TooShort);
        } else if mhlen > 129 {
            return Err(DecodeError::TooLong);
        } else {
            let digest_len = (mhlen - 2) as u8;
            if digest_len != self.bytes[1] {
                return Err(DecodeError::InvalidDigestLength(self.bytes[1], digest_len));
            }
        }

        let fn_code = match HashType::from_code(self.bytes[0]) {
            None => return Err(DecodeError::UnknownCode(self.bytes[0])),
            Some(code) => code,
        };

        let decoded = DecodedMultiHash {
            code: fn_code,
            name: fn_code.to_str(),
            length: self.bytes[1],
            digest: &self.bytes[2..],
        };
        Ok(decoded)

    }
    */

    pub fn to_base58_string(&self) -> String {
        self.bytes.to_base58()
    }
}

fn is_valid_code(code: u8) -> bool {
    is_app_code(code) || HashType::from_code(code).is_some()
}

fn is_app_code(code: u8) -> bool {
    code <= 0x0f
}

/*
pub struct DecodedMultiHash<'a> {
    code: HashType,
    name: &'static str,
    length: u8,
    digest: &'a [u8],
}

#[derive(Debug)]
pub enum DecodeError {
    UnknownCode(u8),
    TooShort,
    TooLong,
    InvalidDigestLength(u8, u8), // (stated, actual)
}
*/

#[cfg(test)]
mod tests {
    use super::{multihash, Multihash, HashType};
    use rustc_serialize::hex::FromHex;
    use rust_base58::ToBase58;

    struct TestCase {
        hexstr: &'static str,
        hash_type: HashType,
    }

    impl TestCase {
        fn new(s: &'static str, ty: HashType) -> TestCase {
            TestCase { hexstr: s, hash_type: ty }
        }
    }

    static SHA1_STR: &'static str = "a228821137dacdbcd3ba5fa264f918fd
                                     a6223f7b";

    static SHA256_STR: &'static str = "95071c8e1ad3c7a016b30fd25853c1d9\
                                       e346ee7ee4afc977f554b139f71f4f30";

    static SHA512_STR: &'static str = "3912d4c1777934091ed95a15278b351e\
                                       00efa51f524af9af3320f0f6a227d551\
                                       76b1399684d15a05f03d1e519a9a9aa5\
                                       2a812a6d9bc63e5b485d6fd7ebf72114";

    #[test]
    fn test_multihash() {
        let abc = b"ABC";
        let sha1_hex = "3c01bdbb26f358bab27f267924aa2c9a03fcfdb8";
        let sha256_hex = "b5d4045c3f466fa91fe2cc6abe79232a1a57cdf104f7a26e716e0a1e2789df78";
        let sha512_hex = "397118fdac8d83ad98813c50759c85b8c47565d8268bf10da483153b747a7474\
                          3a58a90e85aa9f705ce6984ffc128db567489817e4092d050d8a1cc596ddc119";

        let sha1 = multihash(abc, HashType::SHA1);
        let mut sha1_bytes = hexstr_to_vec(sha1_hex);
        let mut expected_bytes = vec![0x11, 20];
        expected_bytes.append(&mut sha1_bytes);
        assert_eq!(sha1.as_bytes(), &expected_bytes[..]);

        let sha256 = multihash(abc, HashType::SHA2_256);
        let mut sha256_bytes = hexstr_to_vec(sha256_hex);
        let mut expected_bytes = vec![0x12, 32];
        expected_bytes.append(&mut sha256_bytes);
        assert_eq!(sha256.as_bytes(), &expected_bytes[..]);

        let sha512 = multihash(abc, HashType::SHA2_512);
        let mut sha512_bytes = hexstr_to_vec(sha512_hex);
        let mut expected_bytes = vec![0x13, 64];
        expected_bytes.append(&mut sha512_bytes);
        assert_eq!(sha512.as_bytes(), &expected_bytes[..]);
    }

    fn hexstr_to_vec(hexstr: &'static str) -> Vec<u8> {
        match hexstr.from_hex() {
            Err(e) => panic!("from_hex() failed with error: {:?}", e),
            Ok(vec) => vec,
        }
    }

    #[test]
    fn test_encode() {
        let mut cases = Vec::new();

        cases.push(TestCase::new(SHA1_STR, HashType::SHA1));
        cases.push(TestCase::new(SHA256_STR, HashType::SHA2_256));
        cases.push(TestCase::new(SHA512_STR, HashType::SHA2_512));

        for case in cases {
            let v = hexstr_to_vec(case.hexstr);

            let mut expected = Vec::new();
            expected.push(case.hash_type.code());
            expected.push(v.len() as u8);
            expected.extend(&v[..]);

            let mh = Multihash::encode(&v[..], case.hash_type.code()).unwrap();
            assert_eq!(&expected[..], mh.as_bytes());
        }
    }

    /*
    #[test]
    fn test_decode() {
        let mut cases = Vec::new();

        cases.push(TestCase::new(SHA1_STR, HashType::SHA1));
        cases.push(TestCase::new(SHA256_STR, HashType::SHA2_256));
        cases.push(TestCase::new(SHA512_STR, HashType::SHA2_512));

        for case in cases {
            let v = hexstr_to_vec(case.hexstr);
            let digest_length = v.len() as u8;
            let mh = Multihash::encode(&v[..], case.hash_type).unwrap();
            let hash_name = case.hash_type.to_str();

            match mh.decode() {
                Err(e) => panic!("Error decoding: {:?}", e),
                Ok(decoded) => {
                    assert_eq!(decoded.code, case.hash_type);
                    assert_eq!(decoded.name, hash_name);
                    assert_eq!(decoded.length, digest_length);
                    assert_eq!(decoded.digest, &v[..]);
                },
            }
        }
    }
    */

    #[test]
    fn test_from_base58_str() {
        assert!(Multihash::from_base58_str("Invalid base58!!!").is_err());

        // Errors because digest length of SHA1 is 20, not 5
        let x = &[0x11, 5, 1, 2, 3, 4, 5];
        assert!(Multihash::from_base58_str(&x.to_base58()).is_err());

        let x = &[0x11, 20, 1, 2, 3, 4, 5];
        assert!(Multihash::from_base58_str(&x.to_base58()).is_err());

        let x = &[0x11, 20, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                  0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
        assert!(Multihash::from_base58_str(&x.to_base58()).is_ok());
    }
}
<|endoftext|>


<commit_before>extern crate antidote;
extern crate chrono;
extern crate config;
extern crate data_encoding;
#[macro_use]
extern crate failure;
extern crate fern;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[cfg(test)]
extern crate rand;
extern crate reqwest;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
#[cfg(test)]
extern crate spectral;
#[cfg(test)]
extern crate test_server;

mod ctapi;
mod http;
mod logging;
mod settings;

use crate::ctapi::close::close;
use crate::ctapi::data::data;
use crate::ctapi::init::init;
use crate::ctapi::status::Status;
use crate::settings::Settings;
use antidote::RwLock;
use std::panic;

lazy_static! {
    pub(crate) static ref CONFIG: RwLock<Settings> =
        RwLock::new(Settings::init().expect("Failed to init configuration!"));
}

#[no_mangle]
pub extern "system" fn CT_init(ctn: u16, pn: u16) -> i8 {
    logging::init();

    debug!("CT_init(ctn: {}, pn: {})", ctn, pn);
    let status: i8 = match init(ctn, pn) {
        Ok(status) => status.value(),
        Err(why) => {
            error!("Failure during CT_init!");
            debug!("{}", why);
            Status::ERR_HTSI.value()
        }
    };

    debug!("Returning {}", status);
    status
}

#[no_mangle]
pub extern "system" fn CT_data(
    ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> i8 {
    logging::init();

    if dad.is_null() {
        error!("Null pointer passed into CT_data() as dad");
        return Status::ERR_HTSI.value();
    }

    if sad.is_null() {
        error!("Null pointer passed into CT_data() as sad");
        return Status::ERR_HTSI.value();
    }

    if lenr.is_null() {
        error!("Null pointer passed into CT_data() as lenr");
        return Status::ERR_HTSI.value();
    }

    if response.is_null() {
        error!("Null pointer passed into CT_data() as response");
        return Status::ERR_HTSI.value();
    }

    debug!("CT_data(ctn: {})", ctn);
    let status: i8 = match data(ctn, dad, sad, lenc, command, lenr, response) {
        Ok(status) => status.value(),
        Err(why) => {
            error!("Failure during CT_data!");
            debug!("{}", why);
            Status::ERR_HTSI.value()
        }
    };

    debug!("Returning {}", status);
    status
}

#[no_mangle]
pub extern "system" fn CT_close(ctn: u16) -> i8 {
    logging::init();

    debug!("CT_close(ctn: {})", ctn);
    let status = match panic::catch_unwind(|| close(ctn)) {
        Ok(result) => match result {
            Ok(status) => status.value(),
            Err(why) => {
                error!("Failure during CT_close!");
                debug!("{}", why);
                Status::ERR_HTSI.value()
            }
        },
        Err(_) => {
            error!("Caught panic!");
            Status::ERR_HTSI.value()
        }
    };

    debug!("Returning {}", status);
    status
}
<commit_msg>move catch_unwind from ct_close to ct_data<commit_after>extern crate antidote;
extern crate chrono;
extern crate config;
extern crate data_encoding;
#[macro_use]
extern crate failure;
extern crate fern;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[cfg(test)]
extern crate rand;
extern crate reqwest;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
#[cfg(test)]
extern crate spectral;
#[cfg(test)]
extern crate test_server;

mod ctapi;
mod http;
mod logging;
mod settings;

use crate::ctapi::close::close;
use crate::ctapi::data::data;
use crate::ctapi::init::init;
use crate::ctapi::status::Status;
use crate::settings::Settings;
use antidote::RwLock;
use std::panic;

lazy_static! {
    pub(crate) static ref CONFIG: RwLock<Settings> =
        RwLock::new(Settings::init().expect("Failed to init configuration!"));
}

#[no_mangle]
pub extern "system" fn CT_init(ctn: u16, pn: u16) -> i8 {
    logging::init();

    debug!("CT_init(ctn: {}, pn: {})", ctn, pn);
    let status: i8 = match init(ctn, pn) {
        Ok(status) => status.value(),
        Err(why) => {
            error!("Failure during CT_init!");
            debug!("{}", why);
            Status::ERR_HTSI.value()
        }
    };

    debug!("Returning {}", status);
    status
}

#[no_mangle]
pub extern "system" fn CT_data(
    ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> i8 {
    logging::init();

    if dad.is_null() {
        error!("Null pointer passed into CT_data() as dad");
        return Status::ERR_HTSI.value();
    }

    if sad.is_null() {
        error!("Null pointer passed into CT_data() as sad");
        return Status::ERR_HTSI.value();
    }

    if lenr.is_null() {
        error!("Null pointer passed into CT_data() as lenr");
        return Status::ERR_HTSI.value();
    }

    if response.is_null() {
        error!("Null pointer passed into CT_data() as response");
        return Status::ERR_HTSI.value();
    }

    debug!("CT_data(ctn: {})", ctn);
    let status: i8 =
        match panic::catch_unwind(|| data(ctn, dad, sad, lenc, command, lenr, response)) {
            Ok(result) => match result {
                Ok(status) => status.value(),
                Err(why) => {
                    error!("Failure during CT_data!");
                    debug!("{}", why);
                    Status::ERR_HTSI.value()
                }
            },
            Err(_) => {
                error!("Caught panic!");
                Status::ERR_HTSI.value()
            }
        };

    debug!("Returning {}", status);
    status
}

#[no_mangle]
pub extern "system" fn CT_close(ctn: u16) -> i8 {
    logging::init();

    debug!("CT_close(ctn: {})", ctn);
    let status = match close(ctn) {
        Ok(status) => status.value(),
        Err(why) => {
            error!("Failure during CT_close!");
            debug!("{}", why);
            Status::ERR_HTSI.value()
        }
    };

    debug!("Returning {}", status);
    status
}
<|endoftext|>


<commit_before>#![recursion_limit = "1024"]
#[cfg(test)]
extern crate float_cmp;

#[cfg(test)]
#[macro_use]
extern crate quickcheck;

#[macro_use]
extern crate error_chain;

extern crate combine;

pub mod ast;
pub mod token;
pub mod lexer;
pub mod parser;
pub mod error;
pub mod ops;
<commit_msg>Reduce API surface<commit_after>#![recursion_limit = "1024"]
#[cfg(test)]
extern crate float_cmp;

#[cfg(test)]
#[macro_use]
extern crate quickcheck;

#[macro_use]
extern crate error_chain;

#[macro_use]
extern crate combine;

mod ast;
mod token;
mod ops;

pub mod lexer;
pub mod parser;
pub mod error;
<|endoftext|>


<commit_before>// Copyright 2016 rust-freqdist Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Implementation of a Frequency Distribution in Rust. Keeps track of how many
//! times an object appears in a larger context (for example, how many times a
//! word appears in a piece of text). The underlying data structure of the
//! Frequency Distribution is a HashMap, so the object that is being counted
//! must be hashable.
//!
//! # Example
//!
//! ```
//! # use freqdist::FrequencyDistribution;
//! #
//! let mut fdist: FrequencyDistribution<&str> = FrequencyDistribution::new();
//!
//! fdist.insert("hello");
//! fdist.insert("hello");
//! fdist.insert("goodbye");
//!
//! assert_eq!(fdist.get(&"hello"), 2);
//! ```

#![warn(missing_docs)]
#![cfg_attr(test, feature(test))]

#[cfg(test)]
extern crate test;

use std::ops::Index;
use std::default::Default;
use std::hash::{Hasher, Hash, BuildHasher, SipHasher};
use std::iter::{FromIterator, IntoIterator};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::collections::hash_map::{Keys, IntoIter, Iter, RandomState};

static ZERO: usize = 0;

#[allow(missing_docs)]
pub struct FrequencyDistribution<K, S = RandomState> {
  hashmap: HashMap<K, usize, S>,
  sum_counts: usize,
}

impl<K, H = SipHasher, S = RandomState> FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher,
        S: BuildHasher<Hasher = H>
{
  /// Creates a new FrequencyDistrbution with a hasher and size, where
  /// the size is known or can be estimated.
  #[inline(always)]
  pub fn with_capacity_and_hasher(size: usize, state: S) -> FrequencyDistribution<K, S> {
    FrequencyDistribution {
      hashmap: HashMap::with_capacity_and_hasher(size, state),
      sum_counts: 0,
    }
  }

  /// Creates a new FrequencyDistribution with a hasher and default size.
  #[inline(always)]
  pub fn with_hasher(state: S) -> FrequencyDistribution<K, S> {
    FrequencyDistribution {
      hashmap: HashMap::with_hasher(state),
      sum_counts: 0,
    }
  }

  /// Iterator over the keys.
  #[inline(always)]
  pub fn keys(&self) -> Keys<K, usize> {
    self.hashmap.keys()
  }

  /// Iterator over the key, frequency pairs.
  #[inline(always)]
  pub fn iter(&self) -> Iter<K, usize> {
    self.hashmap.iter()
  }

  /// Iterator over the non-zero frequency keys.
  ///
  /// # Example
  ///
  /// ```
  /// # use std::iter::{FromIterator, IntoIterator};
  /// # use freqdist::FrequencyDistribution;
  /// #
  /// let existing = vec![
  ///   ("shoes", 1),
  ///   ("scarf", 0),
  ///   ("shirt", 13),
  ///   ("pants", 4)
  /// ];
  ///
  /// let fdist: FrequencyDistribution<&str> =
  ///   FromIterator::from_iter(existing.into_iter());
  /// let mut iter = fdist.iter_non_zero();
  ///
  /// assert!(iter.next().is_some());
  /// assert!(iter.next().is_some());
  /// assert!(iter.next().is_some());
  /// assert!(iter.next().is_none());
  /// ```
  #[inline(always)]
  pub fn iter_non_zero(&self) -> NonZeroKeysIter<K> {
    NonZeroKeysIter { iter: self.iter() }
  }

  /// Sum of the total number of items counted thus far.
  #[inline(always)]
  pub fn sum_counts(&self) -> usize {
    self.sum_counts
  }

  /// Returns the number of entries in the distribution
  #[inline(always)]
  pub fn len(&self) -> usize {
    self.hashmap.len()
  }

  /// Gets the frequency in which the key occurs.
  #[inline(always)]
  pub fn get<Q: ?Sized>(&self, k: &Q) -> usize
    where K: Borrow<Q>,
          Q: Hash + Eq
  {
    self[k]
  }

  /// Clears the counts of all keys and clears all keys from
  /// the distribution.
  #[inline(always)]
  pub fn clear(&mut self) {
    self.hashmap.clear()
  }

  /// Updates the frequency of the value found with the key if it
  /// already exists. Otherwise, inserts the key sizeo the hashmap,
  /// and sets its frequency to 1.
  #[inline(always)]
  pub fn insert(&mut self, k: K) {
    self.insert_or_incr_by(k, 1);
  }

  /// Removes an item and its associated counts.
  #[inline(always)]
  pub fn remove<Q: ?Sized>(&mut self, k: &Q)
    where K: Borrow<Q>,
          Q: Hash + Eq
  {
    match self.hashmap.remove(k) {
      Some(count) => self.sum_counts -= count,
      None => (),
    }
  }

  /// Inserts a value sizeo the hashmap if it does not exist with a new quantity
  /// specified by the increment. If the value already exists, increments by
  /// the specified amount.
  #[inline]
  fn insert_or_incr_by(&mut self, k: K, incr: usize) {
    if !self.hashmap.contains_key(&k) {
      self.hashmap.insert(k, incr);
    } else {
      *self.hashmap.get_mut(&k).unwrap() += incr;
    }

    self.sum_counts += incr;
  }
}

impl<K, H = SipHasher, S = RandomState> FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher + Default,
        S: BuildHasher<Hasher = H> + Default
{
  /// Creates a new FrequencyDistribution where the size of the
  /// HashMap is unknown.
  #[inline(always)]
  pub fn new() -> FrequencyDistribution<K, S> {
    FrequencyDistribution::with_hasher(Default::default())
  }

  /// Creates a new FrequencyDistribution where the size of the HashMap
  /// is known, or a estimate can be made.
  #[inline(always)]
  pub fn with_capacity(size: usize) -> FrequencyDistribution<K, S> {
    FrequencyDistribution::with_capacity_and_hasher(size, Default::default())
  }
}

impl<K, H = SipHasher, S = RandomState> Default for FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher + Default,
        S: BuildHasher<Hasher = H> + Default
{
  /// Creates a default FrequencyDistribution.
  #[inline(always)]
  fn default() -> FrequencyDistribution<K, S> {
    FrequencyDistribution::new()
  }
}

impl<K, H, S> FromIterator<(K, usize)> for FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher,
        S: BuildHasher<Hasher = H> + Default
{
  /// Iterates through an iterator, and creates a new FrequencyDistribution from
  /// it. The iterator should be an iterator over keys and frequencies. If a
  /// upper bounded `size_hsize` is available, then it is used, otherwise the lower
  /// bounded `size_hsize` is used.
  ///
  /// # Example
  ///
  /// ```
  /// # use std::iter::FromIterator;
  /// # use freqdist::FrequencyDistribution;
  /// #
  /// let existing = vec![
  ///   ("apples", 3),
  ///   ("oranges", 4),
  ///   ("bannana", 7)
  /// ];
  ///
  /// let fdist: FrequencyDistribution<&str> =
  ///   FromIterator::from_iter(existing.into_iter());
  ///
  /// assert_eq!(fdist.get(&"apples"), 3);
  /// assert_eq!(fdist.get(&"oranges"), 4);
  /// assert_eq!(fdist.get(&"bannana"), 7);
  /// ```
  fn from_iter<T>(iter: T) -> FrequencyDistribution<K, S>
    where T: IntoIterator<Item = (K, usize)>
  {
    let iterator = iter.into_iter();
    let mut fdist = if iterator.size_hint().1.is_some() {
      FrequencyDistribution::with_capacity_and_hasher(iterator.size_hint().1.unwrap(),
                                                      Default::default())
    } else {
      FrequencyDistribution::with_capacity_and_hasher(iterator.size_hint().0, Default::default())
    };

    for (k, freq) in iterator {
      fdist.insert_or_incr_by(k, freq);
    }

    fdist
  }
}

impl<K, H, S> Extend<(K, usize)> for FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher,
        S: BuildHasher<Hasher = H>
{
  /// Extends the hashmap by adding the keys or updating the frequencies of the keys.
  fn extend<T>(&mut self, iter: T)
    where T: IntoIterator<Item = (K, usize)>
  {
    for (k, freq) in iter.into_iter() {
      self.insert_or_incr_by(k, freq);
    }
  }
}

impl<K, H, S> IntoIterator for FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher,
        S: BuildHasher<Hasher = H>
{
  type Item = (K, usize);
  type IntoIter = IntoIter<K, usize>;

  /// Consumes the distribution, and creates an iterator over the
  /// (Key, Quantity: usize) pairs.
  #[inline]
  fn into_iter(self) -> IntoIter<K, usize> {
    self.hashmap.into_iter()
  }
}

impl<'a, K, H, S, Q: ?Sized> Index<&'a Q> for FrequencyDistribution<K, S>
  where K: Eq + Hash + Borrow<Q>,
        H: Hasher,
        S: BuildHasher<Hasher = H>,
        Q: Eq + Hash
{
  type Output = usize;

  #[inline]
  fn index<'b>(&'b self, index: &Q) -> &'b usize {
    self.hashmap.get(index).unwrap_or(&ZERO)
  }
}

/// Iterator over entries with non-zero quantities.
pub struct NonZeroKeysIter<'a, K: 'a> {
  iter: Iter<'a, K, usize>,
}

impl<'a, K: 'a> Iterator for NonZeroKeysIter<'a, K> {
  type Item = &'a K;

  #[inline(always)]
  fn next(&mut self) -> Option<&'a K> {
    loop {
      match self.iter.next() {
        Some((k, c)) if *c > 0 => return Some(k),
        None => return None,
        _ => (),
      }
    }
  }
}

#[test]
fn smoke_test_frequency_distribution_insert() {
  let words = vec!["alpha", "beta"];
  let mut dist: FrequencyDistribution<&str> = FrequencyDistribution::new();

  dist.insert(words[0]);

  assert_eq!(dist.get(&words[0]), 1);

  dist.insert(words[1]);

  assert_eq!(dist.get(&words[1]), 1);

  for _ in 0..7u32 {
    dist.insert(words[0]);
  }

  assert_eq!(dist.get(&words[0]), 8);
}

#[test]
fn smoke_test_frequency_distribution_iter() {
  let words = vec![("a", 50usize), ("b", 100usize), ("c", 75usize), ("d", 0usize)];
  let dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.get(&"a"), 50);
  assert_eq!(dist.get(&"b"), 100);
  assert_eq!(dist.get(&"c"), 75);

  let mut iter = dist.iter_non_zero();

  assert!(iter.next().is_some());
  assert!(iter.next().is_some());
  assert!(iter.next().is_some());
  assert!(iter.next().is_none());

  assert_eq!(dist.sum_counts(), 225);
}

#[test]
fn smoke_test_frequency_distribution_remove() {
  let words = vec![("a", 50usize), ("b", 100usize), ("c", 25usize)];
  let mut dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.get(&"a"), 50);

  dist.remove(&"a");

  assert_eq!(dist.get(&"a"), 0);
  assert_eq!(dist.sum_counts(), 125);
}

#[test]
fn smoke_test_frequency_sum_counts() {
  let words = vec![("a", 7usize), ("b", 5usize), ("c", 8usize), ("d", 3usize)];
  let mut dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.sum_counts(), 23);

  dist.insert("e");

  assert_eq!(dist.sum_counts(), 24);
}
<commit_msg>update for rust nightly<commit_after>// Copyright 2016 rust-freqdist Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Implementation of a Frequency Distribution in Rust. Keeps track of how many
//! times an object appears in a larger context (for example, how many times a
//! word appears in a piece of text). The underlying data structure of the
//! Frequency Distribution is a HashMap, so the object that is being counted
//! must be hashable.
//!
//! # Example
//!
//! ```
//! # use freqdist::FrequencyDistribution;
//! #
//! let mut fdist: FrequencyDistribution<&str> = FrequencyDistribution::new();
//!
//! fdist.insert("hello");
//! fdist.insert("hello");
//! fdist.insert("goodbye");
//!
//! assert_eq!(fdist.get(&"hello"), 2);
//! ```

#![warn(missing_docs)]
#![cfg_attr(test, feature(test))]

#[cfg(test)]
extern crate test;

use std::ops::Index;
use std::default::Default;
use std::hash::{Hasher, Hash, BuildHasher};
use std::iter::{FromIterator, IntoIterator};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::collections::hash_map::{Keys, IntoIter, Iter, RandomState};

static ZERO: usize = 0;

#[allow(missing_docs)]
pub struct FrequencyDistribution<K, S = RandomState> {
  hashmap: HashMap<K, usize, S>,
  sum_counts: usize,
}

impl<K, H, S> FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher,
        S: BuildHasher<Hasher = H>
{
  /// Creates a new FrequencyDistrbution with a hasher and size, where
  /// the size is known or can be estimated.
  #[inline(always)]
  pub fn with_capacity_and_hasher(size: usize, state: S) -> FrequencyDistribution<K, S> {
    FrequencyDistribution {
      hashmap: HashMap::with_capacity_and_hasher(size, state),
      sum_counts: 0,
    }
  }

  /// Creates a new FrequencyDistribution with a hasher and default size.
  #[inline(always)]
  pub fn with_hasher(state: S) -> FrequencyDistribution<K, S> {
    FrequencyDistribution {
      hashmap: HashMap::with_hasher(state),
      sum_counts: 0,
    }
  }

  /// Iterator over the keys.
  #[inline(always)]
  pub fn keys(&self) -> Keys<K, usize> {
    self.hashmap.keys()
  }

  /// Iterator over the key, frequency pairs.
  #[inline(always)]
  pub fn iter(&self) -> Iter<K, usize> {
    self.hashmap.iter()
  }

  /// Iterator over the non-zero frequency keys.
  ///
  /// # Example
  ///
  /// ```
  /// # use std::iter::{FromIterator, IntoIterator};
  /// # use freqdist::FrequencyDistribution;
  /// #
  /// let existing = vec![
  ///   ("shoes", 1),
  ///   ("scarf", 0),
  ///   ("shirt", 13),
  ///   ("pants", 4)
  /// ];
  ///
  /// let fdist: FrequencyDistribution<&str> =
  ///   FromIterator::from_iter(existing.into_iter());
  /// let mut iter = fdist.iter_non_zero();
  ///
  /// assert!(iter.next().is_some());
  /// assert!(iter.next().is_some());
  /// assert!(iter.next().is_some());
  /// assert!(iter.next().is_none());
  /// ```
  #[inline(always)]
  pub fn iter_non_zero(&self) -> NonZeroKeysIter<K> {
    NonZeroKeysIter { iter: self.iter() }
  }

  /// Sum of the total number of items counted thus far.
  #[inline(always)]
  pub fn sum_counts(&self) -> usize {
    self.sum_counts
  }

  /// Returns the number of entries in the distribution
  #[inline(always)]
  pub fn len(&self) -> usize {
    self.hashmap.len()
  }

  /// Gets the frequency in which the key occurs.
  #[inline(always)]
  pub fn get<Q: ?Sized>(&self, k: &Q) -> usize
    where K: Borrow<Q>,
          Q: Hash + Eq
  {
    self[k]
  }

  /// Clears the counts of all keys and clears all keys from
  /// the distribution.
  #[inline(always)]
  pub fn clear(&mut self) {
    self.hashmap.clear()
  }

  /// Updates the frequency of the value found with the key if it
  /// already exists. Otherwise, inserts the key sizeo the hashmap,
  /// and sets its frequency to 1.
  #[inline(always)]
  pub fn insert(&mut self, k: K) {
    self.insert_or_incr_by(k, 1);
  }

  /// Removes an item and its associated counts.
  #[inline(always)]
  pub fn remove<Q: ?Sized>(&mut self, k: &Q)
    where K: Borrow<Q>,
          Q: Hash + Eq
  {
    match self.hashmap.remove(k) {
      Some(count) => self.sum_counts -= count,
      None => (),
    }
  }

  /// Inserts a value sizeo the hashmap if it does not exist with a new quantity
  /// specified by the increment. If the value already exists, increments by
  /// the specified amount.
  #[inline]
  fn insert_or_incr_by(&mut self, k: K, incr: usize) {
    if !self.hashmap.contains_key(&k) {
      self.hashmap.insert(k, incr);
    } else {
      *self.hashmap.get_mut(&k).unwrap() += incr;
    }

    self.sum_counts += incr;
  }
}

impl<K, H, S> FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher + Default,
        S: BuildHasher<Hasher = H> + Default
{
  /// Creates a new FrequencyDistribution where the size of the
  /// HashMap is unknown.
  #[inline(always)]
  pub fn new() -> FrequencyDistribution<K, S> {
    FrequencyDistribution::with_hasher(Default::default())
  }

  /// Creates a new FrequencyDistribution where the size of the HashMap
  /// is known, or a estimate can be made.
  #[inline(always)]
  pub fn with_capacity(size: usize) -> FrequencyDistribution<K, S> {
    FrequencyDistribution::with_capacity_and_hasher(size, Default::default())
  }
}

impl<K, H, S> Default for FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher + Default,
        S: BuildHasher<Hasher = H> + Default
{
  /// Creates a default FrequencyDistribution.
  #[inline(always)]
  fn default() -> FrequencyDistribution<K, S> {
    FrequencyDistribution::new()
  }
}

impl<K, H, S> FromIterator<(K, usize)> for FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher,
        S: BuildHasher<Hasher = H> + Default
{
  /// Iterates through an iterator, and creates a new FrequencyDistribution from
  /// it. The iterator should be an iterator over keys and frequencies. If a
  /// upper bounded `size_hsize` is available, then it is used, otherwise the lower
  /// bounded `size_hsize` is used.
  ///
  /// # Example
  ///
  /// ```
  /// # use std::iter::FromIterator;
  /// # use freqdist::FrequencyDistribution;
  /// #
  /// let existing = vec![
  ///   ("apples", 3),
  ///   ("oranges", 4),
  ///   ("bannana", 7)
  /// ];
  ///
  /// let fdist: FrequencyDistribution<&str> =
  ///   FromIterator::from_iter(existing.into_iter());
  ///
  /// assert_eq!(fdist.get(&"apples"), 3);
  /// assert_eq!(fdist.get(&"oranges"), 4);
  /// assert_eq!(fdist.get(&"bannana"), 7);
  /// ```
  fn from_iter<T>(iter: T) -> FrequencyDistribution<K, S>
    where T: IntoIterator<Item = (K, usize)>
  {
    let iterator = iter.into_iter();
    let mut fdist = if iterator.size_hint().1.is_some() {
      FrequencyDistribution::with_capacity_and_hasher(iterator.size_hint().1.unwrap(),
                                                      Default::default())
    } else {
      FrequencyDistribution::with_capacity_and_hasher(iterator.size_hint().0, Default::default())
    };

    for (k, freq) in iterator {
      fdist.insert_or_incr_by(k, freq);
    }

    fdist
  }
}

impl<K, H, S> Extend<(K, usize)> for FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher,
        S: BuildHasher<Hasher = H>
{
  /// Extends the hashmap by adding the keys or updating the frequencies of the keys.
  fn extend<T>(&mut self, iter: T)
    where T: IntoIterator<Item = (K, usize)>
  {
    for (k, freq) in iter.into_iter() {
      self.insert_or_incr_by(k, freq);
    }
  }
}

impl<K, H, S> IntoIterator for FrequencyDistribution<K, S>
  where K: Eq + Hash,
        H: Hasher,
        S: BuildHasher<Hasher = H>
{
  type Item = (K, usize);
  type IntoIter = IntoIter<K, usize>;

  /// Consumes the distribution, and creates an iterator over the
  /// (Key, Quantity: usize) pairs.
  #[inline]
  fn into_iter(self) -> IntoIter<K, usize> {
    self.hashmap.into_iter()
  }
}

impl<'a, K, H, S, Q: ?Sized> Index<&'a Q> for FrequencyDistribution<K, S>
  where K: Eq + Hash + Borrow<Q>,
        H: Hasher,
        S: BuildHasher<Hasher = H>,
        Q: Eq + Hash
{
  type Output = usize;

  #[inline]
  fn index<'b>(&'b self, index: &Q) -> &'b usize {
    self.hashmap.get(index).unwrap_or(&ZERO)
  }
}

/// Iterator over entries with non-zero quantities.
pub struct NonZeroKeysIter<'a, K: 'a> {
  iter: Iter<'a, K, usize>,
}

impl<'a, K: 'a> Iterator for NonZeroKeysIter<'a, K> {
  type Item = &'a K;

  #[inline(always)]
  fn next(&mut self) -> Option<&'a K> {
    loop {
      match self.iter.next() {
        Some((k, c)) if *c > 0 => return Some(k),
        None => return None,
        _ => (),
      }
    }
  }
}

#[test]
fn smoke_test_frequency_distribution_insert() {
  let words = vec!["alpha", "beta"];
  let mut dist: FrequencyDistribution<&str> = FrequencyDistribution::new();

  dist.insert(words[0]);

  assert_eq!(dist.get(&words[0]), 1);

  dist.insert(words[1]);

  assert_eq!(dist.get(&words[1]), 1);

  for _ in 0..7u32 {
    dist.insert(words[0]);
  }

  assert_eq!(dist.get(&words[0]), 8);
}

#[test]
fn smoke_test_frequency_distribution_iter() {
  let words = vec![("a", 50usize), ("b", 100usize), ("c", 75usize), ("d", 0usize)];
  let dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.get(&"a"), 50);
  assert_eq!(dist.get(&"b"), 100);
  assert_eq!(dist.get(&"c"), 75);

  let mut iter = dist.iter_non_zero();

  assert!(iter.next().is_some());
  assert!(iter.next().is_some());
  assert!(iter.next().is_some());
  assert!(iter.next().is_none());

  assert_eq!(dist.sum_counts(), 225);
}

#[test]
fn smoke_test_frequency_distribution_remove() {
  let words = vec![("a", 50usize), ("b", 100usize), ("c", 25usize)];
  let mut dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.get(&"a"), 50);

  dist.remove(&"a");

  assert_eq!(dist.get(&"a"), 0);
  assert_eq!(dist.sum_counts(), 125);
}

#[test]
fn smoke_test_frequency_sum_counts() {
  let words = vec![("a", 7usize), ("b", 5usize), ("c", 8usize), ("d", 3usize)];
  let mut dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.sum_counts(), 23);

  dist.insert("e");

  assert_eq!(dist.sum_counts(), 24);
}
<|endoftext|>


<commit_before>extern crate libc;

use std::mem;
use std::path::Path;
use std::ptr::copy;
use std::slice;

use libc::{c_char, c_uchar, c_int, c_ulonglong, c_void, size_t};

const RLITE_REPLY_STRING:c_int = 1;
const RLITE_REPLY_ARRAY:c_int = 2;
const RLITE_REPLY_INTEGER:c_int = 3;
const RLITE_REPLY_NIL:c_int = 4;
const RLITE_REPLY_STATUS:c_int = 5;
const RLITE_REPLY_ERROR:c_int = 6;

#[repr(C)]
struct RliteReply {
    rtype: c_int,
    integer: c_ulonglong,
    len: c_int,
    st: *const c_uchar,
    elements: size_t,
    element: *const *const RliteReply,
}

#[derive(Clone, Debug, PartialEq)]
pub enum Reply {
    Nil,
    Integer(i64),
    Data(Vec<u8>),
    Status(String),
    Array(Vec<Reply>),
}

macro_rules! str_to_vec {
    ($str: expr) => {{
        let len = (*$str).len as usize;
        let mut v:Vec<u8> = Vec::with_capacity(len);
        v.set_len(len);
        copy((*$str).st, v.as_mut_ptr() as *mut u8, len);
        v
    }}
}

impl Reply {
    fn new(reply: *const RliteReply) -> Result<Self, String> {
        unsafe {
            Ok(match (*reply).rtype {
                RLITE_REPLY_STRING => {
                    Reply::Data(str_to_vec!(reply))
                },
                RLITE_REPLY_STATUS => {
                    Reply::Status(String::from_utf8(str_to_vec!(reply)).unwrap())
                },
                RLITE_REPLY_ERROR => {
                    let s = String::from_utf8(str_to_vec!(reply)).unwrap();
                    return Err(s);
                },
                RLITE_REPLY_NIL => Reply::Nil,
                RLITE_REPLY_INTEGER => Reply::Integer((*reply).integer as i64),
                RLITE_REPLY_ARRAY => {
                    let elements:usize = (*reply).elements as usize;
                    let mut v = Vec::with_capacity(elements);
                    let slice = slice::from_raw_parts((*reply).element, elements);
                    for i in 0..elements {
                        v.push(try!(Reply::new(slice[i])));
                    }
                    Reply::Array(v)
                },
                _ => return Err(format!("Unknown reply type {}", (*reply).rtype)),
            })
        }
    }
}

#[link(name = "hirlite")]
extern {
    fn rliteConnect(path: *const c_char, port: c_int) -> *mut c_void;
    fn rliteAppendCommandArgv(client: *mut c_void, argc: c_int, argv: *const *const u8, argvlen: *const size_t) -> c_int;
    fn rliteGetReply(context: *const c_void, reply: *mut *const RliteReply) -> c_int;
    fn rliteFreeReplyObject(reply: *const RliteReply);
    fn rliteFree(context: *const c_void);
}

pub struct Rlite {
    rlite: *mut c_void,
}

impl Rlite {
    pub fn memory() -> Self {
        let rlite = unsafe { rliteConnect(":memory:".as_ptr() as *const c_char, 0) };
        Rlite { rlite: rlite }
    }

    pub fn file(path: &Path) -> Result<Self, ()> {
        let f = match path.to_str() {
            Some(p) => p,
            None => return Err(()),
        };
        let rlite = unsafe { rliteConnect(::std::ffi::CString::new(f).unwrap().as_ptr() as *const c_char, 0) };
        if rlite != 0 as *mut _ {
            Ok(Rlite { rlite: rlite })
        } else {
            Err(())
        }
    }

    pub fn write_command(&self, command: &[&[u8]]) -> Result<(), ()> {
        let mut argv = Vec::new();
        let mut argvlen = Vec::new();
        for c in command {
            argv.push(c.as_ptr());
            argvlen.push(c.len() as size_t);
        }
        unsafe {
            if rliteAppendCommandArgv(self.rlite, command.len() as c_int, argv.as_ptr(), argvlen.as_ptr()) == 0 {
                Ok(())
            } else {
                Err(())
            }
        }
    }

    pub fn read_reply(&self) -> Result<Reply, String> {
        unsafe {
            let mut reply = mem::zeroed();
            if rliteGetReply(self.rlite, &mut reply) == 0 {
                if reply == 0 as *mut _ {
                    return Ok(Reply::Nil);
                }
                let r = Reply::new(reply);
                rliteFreeReplyObject(reply);
                r
            } else {
                Err("Failed".to_owned())
            }
        }
    }
}

impl Drop for Rlite {
    fn drop(&mut self) {
        unsafe { rliteFree(self.rlite); }
    }
}
<commit_msg>Add documentation<commit_after>//! # Interface to rlite
//!
//! ## Example
//!
//! ```rust
//! let path = Path::new("db.rld");
//! let rlite = Rlite::file(&path).unwrap();
//!
//! rlite.write_command(&["set".as_bytes(), "key".as_bytes(), "value".as_bytes()]).unwrap();
//! assert_eq!(conn.read_reply().unwrap(), Reply::Status("OK".to_owned()));
//!
//! conn.write_command(&["get".as_bytes(), "key".as_bytes()]).unwrap();
//! assert_eq!(conn.read_reply().unwrap(), Reply::Status("OK".to_owned()));)
//! ```
extern crate libc;

use std::mem;
use std::path::Path;
use std::ptr::copy;
use std::slice;

use libc::{c_char, c_uchar, c_int, c_ulonglong, c_void, size_t};

const RLITE_REPLY_STRING:c_int = 1;
const RLITE_REPLY_ARRAY:c_int = 2;
const RLITE_REPLY_INTEGER:c_int = 3;
const RLITE_REPLY_NIL:c_int = 4;
const RLITE_REPLY_STATUS:c_int = 5;
const RLITE_REPLY_ERROR:c_int = 6;

#[repr(C)]
struct RliteReply {
    rtype: c_int,
    integer: c_ulonglong,
    len: c_int,
    st: *const c_uchar,
    elements: size_t,
    element: *const *const RliteReply,
}

/// A command reply
#[derive(Clone, Debug, PartialEq)]
pub enum Reply {
    Nil,
    Integer(i64),
    Data(Vec<u8>),
    Status(String),
    Array(Vec<Reply>),
}

macro_rules! str_to_vec {
    ($str: expr) => {{
        let len = (*$str).len as usize;
        let mut v:Vec<u8> = Vec::with_capacity(len);
        v.set_len(len);
        copy((*$str).st, v.as_mut_ptr() as *mut u8, len);
        v
    }}
}

impl Reply {
    fn new(reply: *const RliteReply) -> Result<Self, String> {
        unsafe {
            Ok(match (*reply).rtype {
                RLITE_REPLY_STRING => {
                    Reply::Data(str_to_vec!(reply))
                },
                RLITE_REPLY_STATUS => {
                    Reply::Status(String::from_utf8(str_to_vec!(reply)).unwrap())
                },
                RLITE_REPLY_ERROR => {
                    let s = String::from_utf8(str_to_vec!(reply)).unwrap();
                    return Err(s);
                },
                RLITE_REPLY_NIL => Reply::Nil,
                RLITE_REPLY_INTEGER => Reply::Integer((*reply).integer as i64),
                RLITE_REPLY_ARRAY => {
                    let elements:usize = (*reply).elements as usize;
                    let mut v = Vec::with_capacity(elements);
                    let slice = slice::from_raw_parts((*reply).element, elements);
                    for i in 0..elements {
                        v.push(try!(Reply::new(slice[i])));
                    }
                    Reply::Array(v)
                },
                _ => return Err(format!("Unknown reply type {}", (*reply).rtype)),
            })
        }
    }
}

#[link(name = "hirlite")]
extern {
    fn rliteConnect(path: *const c_char, port: c_int) -> *mut c_void;
    fn rliteAppendCommandArgv(client: *mut c_void, argc: c_int, argv: *const *const u8, argvlen: *const size_t) -> c_int;
    fn rliteGetReply(context: *const c_void, reply: *mut *const RliteReply) -> c_int;
    fn rliteFreeReplyObject(reply: *const RliteReply);
    fn rliteFree(context: *const c_void);
}

/// A database connection
pub struct Rlite {
    rlite: *mut c_void,
}

impl Rlite {
    /// Create a new database in memory
    pub fn memory() -> Self {
        let rlite = unsafe { rliteConnect(":memory:".as_ptr() as *const c_char, 0) };
        Rlite { rlite: rlite }
    }

    /// Opens or creates a database in `path`.
    pub fn file(path: &Path) -> Result<Self, ()> {
        let f = match path.to_str() {
            Some(p) => p,
            None => return Err(()),
        };
        let rlite = unsafe { rliteConnect(::std::ffi::CString::new(f).unwrap().as_ptr() as *const c_char, 0) };
        if rlite != 0 as *mut _ {
            Ok(Rlite { rlite: rlite })
        } else {
            Err(())
        }
    }

    /// Executes the command. It returns either success or error, with no detail.
    /// If it succeeded, use `read_reply` to get the response (if any).
    pub fn write_command(&self, command: &[&[u8]]) -> Result<(), ()> {
        let mut argv = Vec::new();
        let mut argvlen = Vec::new();
        for c in command {
            argv.push(c.as_ptr());
            argvlen.push(c.len() as size_t);
        }
        unsafe {
            if rliteAppendCommandArgv(self.rlite, command.len() as c_int, argv.as_ptr(), argvlen.as_ptr()) == 0 {
                Ok(())
            } else {
                Err(())
            }
        }
    }

    /// Reads the response of the first unread command.
    /// Each command response is appended to a queue. This command pops from
    /// that queue.
    pub fn read_reply(&self) -> Result<Reply, String> {
        unsafe {
            let mut reply = mem::zeroed();
            if rliteGetReply(self.rlite, &mut reply) == 0 {
                if reply == 0 as *mut _ {
                    return Ok(Reply::Nil);
                }
                let r = Reply::new(reply);
                rliteFreeReplyObject(reply);
                r
            } else {
                Err("Failed".to_owned())
            }
        }
    }
}

impl Drop for Rlite {
    fn drop(&mut self) {
        unsafe { rliteFree(self.rlite); }
    }
}
<|endoftext|>