<commit_before>extern crate time;

use super::rusqlite::{SqliteResult, SqliteConnection};
use super::Directory;

pub struct Aliases<'a> {
    connection: &'a SqliteConnection,
    path: Path,
    timestamp: u64,
    file_list: Vec<(uint, String)>,
    directory_id_list: Vec<uint>,
    subdirectory: Option<Box<Aliases<'a>>>
}

impl<'a> Aliases<'a> {
    pub fn new(connection: &'a SqliteConnection, path: Path, directory: Directory, timestamp: u64) -> SqliteResult<Aliases<'a>> {
        let mut rows = match directory {
            Directory::Child(id) => {
                let statement = try!(connection.prepare("SELECT MAX(id) FROM alias WHERE directory_id = $1 AND timestamp <= $2 GROUP BY name;"))
                try!(statement.query(&[&(id as i64), &(timestamp as i64)]))
            },
            Directory::Root      => {
                let statement = try!(connection.prepare("SELECT MAX(id) FROM alias WHERE directory_id IS NULL AND timestamp <= $1 GROUP BY name;"))
                try!(statement.query(&[&(timestamp as i64)]))
            }
        };

        let mut directories = match directory {
            Directory::Child(id) => {
                let statement = try!(connection.prepare("SELECT id FROM directory WHERE parent_id = $1;"));
                try!(statement.query(&[&(id as i64)]))
            },
            Directory::Root      => {
                let statement = try!(connection.prepare("SELECT id FROM directory WHERE parent_id IS NULL;"));
                try!(statement.query(&[]))
            }
        };

        let mut alias_id_list: Vec<uint> = Vec::new();
        let mut directory_id_list: Vec<uint> = Vec::new();

        for row in rows {
            alias_id_list.push(try!(row).get::<i64>(0) as uint);
        }

        for directory in directories {
            directory_id_list.push(try!(directory).get::<i64>(0) as uint);
        }

        let file_list: Vec<(uint, String)> = alias_id_list.iter()
            .map(|id| alias_to_file(connection, *id))
            .filter(|option| option.is_some())
            .map(|option| option.unwrap())
            .collect();

        Ok(Aliases {
            connection: connection,
            path: path,
            timestamp: timestamp,
            file_list: file_list,
            directory_id_list: directory_id_list,
            subdirectory: None
        })
    }
}

impl<'a> Iterator<(Path, Box<[uint]>)> for Aliases<'a> {
    fn next(&mut self) -> Option<(Path, Box<[uint]>)> {
        // return file from child directory
        loop {
            match self.subdirectory {
                None              => (),
                Some(ref mut dir) => match dir.next() {
                    None        => (),
                    Some(alias) => return Some(alias)
                }
            }

            match self.directory_id_list.pop() {
                None     => break,
                Some(id) => {
                    let mut directory_path = self.path.clone();
                    directory_path.push(get_directory_name(self.connection, id));

                    self.subdirectory = Aliases::new(
                        self.connection,
                        directory_path,
                        Directory::Child(id),
                        self.timestamp
                    )
                    .ok().map(|alias| box alias)
                }
            }
        }

        // return file from current directory
        match self.file_list.pop() {
            Some((id, name)) => match get_file_block_list(self.connection, id) {
                Ok(boxed_slice) => {
                    let mut file_path = self.path.clone();
                    file_path.push(name);

                    Some((file_path, boxed_slice))
                },
                Err(..)     => None // no opportunity to return error in an iterator
            },
            None            => None
        }
    }
}

fn get_directory_name(connection: &SqliteConnection, directory_id: uint) -> String {
    connection.query_row(
        "SELECT name FROM directory WHERE id = $1;",
        &[&(directory_id as i64)],
        |row| row.get::<String>(0)
    )
}

fn alias_to_file(connection: &SqliteConnection, alias_id: uint) -> Option<(uint, String)> {
    connection.query_row(
        "SELECT file_id, name FROM alias WHERE id = $1;",
        &[&(alias_id as i64)],
        |row| row.get::<Option<i64>>(0).map(|id| (id as uint, row.get::<String>(1)))
    )
}

fn get_file_block_list(connection: &SqliteConnection, file_id: uint) -> SqliteResult<Box<[uint]>> {
    let mut statement = try!(connection.prepare("SELECT id FROM fileblock WHERE file_id = $1;"));
    let mut blocks = try!(statement.query(&[&(file_id as i64)]));
    let mut block_id_list = Vec::new();

    for block in blocks {
        block_id_list.push(try!(block).get::<i64>(0) as uint);
    }

    Ok(block_id_list.into_boxed_slice())
}

pub fn persist_file(connection: &SqliteConnection, directory: Directory, filename: &str, hash: &str, block_id_list: &[uint]) -> SqliteResult<()> {
    let transaction = try!(connection.transaction());

    try!(connection.execute("INSERT INTO file (hash) VALUES ($1);", &[&hash]));

    let file_id = connection.last_insert_rowid();

    for (ordinal, block_id) in block_id_list.iter().enumerate() {
        try!(connection.execute(
            "INSERT INTO fileblock (file_id, block_id, ordinal) VALUES ($1, $2, $3);"
            , &[&(file_id as i64), &(*block_id as i64), &(ordinal as i64)]
        ));
    }

    let alias_query = "INSERT INTO alias (directory_id, file_id, name, timestamp) VALUES ($1, $2, $3, $4);";
    let timestamp = time::get_time().sec;
    let directory_id: Option<i64> = match directory {
        Directory::Root      => None,
        Directory::Child(id) => Some(id as i64)
    };

    try!(connection.execute(alias_query, &[&directory_id, &(file_id as i64), &filename, &(timestamp as i64)]));

    transaction.commit()
}

pub fn persist_block(connection: &SqliteConnection, hash: &str) -> SqliteResult<uint> {
    connection.execute("INSERT INTO block (hash) VALUES ($1);", &[&hash])
}

pub fn file_known(connection: &SqliteConnection, hash: &str) -> bool {
    connection.query_row(
        "SELECT COUNT(id) FROM file WHERE hash = $1;",
        &[&hash],
        |row| row.get::<i64>(0) > 0
    )
}

pub fn block_hash_from_id(connection: &SqliteConnection, id: uint) -> String {
    connection.query_row(
        "SELECT hash FROM block WHERE id = $1;",
        &[&(id as i64)],
        |row| row.get(0)
    )
}

pub fn block_id_from_hash(connection: &SqliteConnection, hash: &str) -> Option<uint> {
    let result: Option<i64> = connection.query_row(
        "SELECT SUM(id) FROM block WHERE hash = $1;",
        &[&hash],
        |row| row.get(0)
    );

    result.map(|signed| signed as uint)
}

pub fn get_directory(connection: &SqliteConnection, parent: Directory, name: &str) -> SqliteResult<Directory> {
    let parent_id: Option<i64> = match parent {
        Directory::Root      => None,
        Directory::Child(id) => Some(id as i64)
    };

    // TODO: escape name!
    let select_query = "SELECT SUM(id) FROM directory WHERE name = $1 AND parent_id = $2;";
    let directory_id: Option<i64> = connection.query_row(select_query, &[&name, &parent_id], |row| row.get(0));

    if directory_id.is_some() {
        return Ok(Directory::Child(directory_id.unwrap() as uint));
    }

    let insert_query = "INSERT INTO directory (parent_id, name) VALUES ($1, $2);";

    connection.execute(insert_query, &[&parent_id, &name])
        .map(|_| Directory::Child(connection.last_insert_rowid() as uint))
}

pub fn set_key(connection: &SqliteConnection, key: &str, value: &str) -> SqliteResult<uint> {
    connection.execute("INSERT INTO setting (key, value) VALUES ($1, $2);", &[&key, &value])
}

pub fn get_key(connection: &SqliteConnection, key: &str) -> Option<String> {
    connection.query_row(
        "SELECT value FROM setting WHERE key = $1;",
        &[&key],
        |row| row.get(0)
    )
}

pub fn setup(connection: &SqliteConnection) -> SqliteResult<()> {
    let queries = [
        "CREATE TABLE directory (
            id        INTEGER PRIMARY KEY,
            parent_id INTEGER,
            name      TEXT NOT NULL,
            FOREIGN KEY(parent_id) REFERENCES directory(id),
            UNIQUE(parent_id, name)
        );",
        "CREATE TABLE file (
            id           INTEGER PRIMARY KEY,
            hash         TEXT NOT NULL
        );",
        "CREATE TABLE alias (
            id           INTEGER PRIMARY KEY,
            directory_id INTEGER,
            file_id      INTEGER,
            name         TEXT NOT NULL,
            timestamp    INTEGER,
            FOREIGN KEY(directory_id) REFERENCES directory(id),
            FOREIGN KEY(file_id) REFERENCES file(id)
        );",
        "CREATE TABLE block (
            id           INTEGER PRIMARY KEY,
            hash         TEXT NOT NULL
        );",
        "CREATE TABLE fileblock (
            id           INTEGER PRIMARY KEY,
            file_id      INTEGER NOT NULL,
            ordinal      INTEGER NOT NULL,
            block_id     INTEGER NOT NULL,
            FOREIGN KEY(file_id) REFERENCES file(id),
            FOREIGN KEY(block_id) REFERENCES block(id)
        );",
        "CREATE TABLE setting (
            key          TEXT PRIMARY KEY,
            value        TEXT
        );"
    ];

    for query in queries.iter() {
        try!(connection.execute(*query, &[]));
    }

    Ok(())
}
<commit_msg>revert refactor of aliases constructor<commit_after>extern crate time;

use super::rusqlite::{SqliteResult, SqliteConnection};
use super::Directory;

pub struct Aliases<'a> {
    connection: &'a SqliteConnection,
    path: Path,
    timestamp: u64,
    file_list: Vec<(uint, String)>,
    directory_id_list: Vec<uint>,
    subdirectory: Option<Box<Aliases<'a>>>
}

impl<'a> Aliases<'a> {
    pub fn new(connection: &'a SqliteConnection, path: Path, directory: Directory, timestamp: u64) -> SqliteResult<Aliases<'a>> {
        let mut row_statement = match directory {
            Directory::Child(..) => try!(connection.prepare("SELECT MAX(id) FROM alias WHERE directory_id = $1 AND timestamp <= $2 GROUP BY name;")),
            Directory::Root      => try!(connection.prepare("SELECT MAX(id) FROM alias WHERE directory_id IS NULL AND timestamp <= $1 GROUP BY name;"))
        };

        let mut rows = match directory {
            Directory::Child(id) => try!(row_statement.query(&[&(id as i64), &(timestamp as i64)])),
            Directory::Root      => try!(row_statement.query(&[&(timestamp as i64)]))
        };

        let mut directory_statement = match directory {
            Directory::Child(..) => try!(connection.prepare("SELECT id FROM directory WHERE parent_id = $1;")),
            Directory::Root      => try!(connection.prepare("SELECT id FROM directory WHERE parent_id IS NULL;"))
        };

        let mut directories = match directory {
            Directory::Child(id) => try!(directory_statement.query(&[&(id as i64)])),
            Directory::Root      => try!(directory_statement.query(&[]))
        };

        let mut alias_id_list = Vec::new();
        let mut directory_id_list = Vec::new();

        for row in rows {
            alias_id_list.push(try!(row).get::<i64>(0) as uint);
        }

        for directory in directories {
            directory_id_list.push(try!(directory).get::<i64>(0) as uint);
        }

        let file_list: Vec<(uint, String)> = alias_id_list.iter()
            .map(|id| alias_to_file(connection, *id))
            .filter(|option| option.is_some())
            .map(|option| option.unwrap())
            .collect();

        Ok(Aliases {
            connection: connection,
            path: path,
            timestamp: timestamp,
            file_list: file_list,
            directory_id_list: directory_id_list,
            subdirectory: None
        })
    }
}

impl<'a> Iterator<(Path, Box<[uint]>)> for Aliases<'a> {
    fn next(&mut self) -> Option<(Path, Box<[uint]>)> {
        // return file from child directory
        loop {
            match self.subdirectory {
                None              => (),
                Some(ref mut dir) => match dir.next() {
                    None        => (),
                    Some(alias) => return Some(alias)
                }
            }

            match self.directory_id_list.pop() {
                None     => break,
                Some(id) => {
                    let mut directory_path = self.path.clone();
                    directory_path.push(get_directory_name(self.connection, id));

                    self.subdirectory = Aliases::new(
                        self.connection,
                        directory_path,
                        Directory::Child(id),
                        self.timestamp
                    )
                    .ok().map(|alias| box alias)
                }
            }
        }

        // return file from current directory
        match self.file_list.pop() {
            Some((id, name)) => match get_file_block_list(self.connection, id) {
                Ok(boxed_slice) => {
                    let mut file_path = self.path.clone();
                    file_path.push(name);

                    Some((file_path, boxed_slice))
                },
                Err(..)     => None // no opportunity to return error in an iterator
            },
            None            => None
        }
    }
}

fn get_directory_name(connection: &SqliteConnection, directory_id: uint) -> String {
    connection.query_row(
        "SELECT name FROM directory WHERE id = $1;",
        &[&(directory_id as i64)],
        |row| row.get::<String>(0)
    )
}

fn alias_to_file(connection: &SqliteConnection, alias_id: uint) -> Option<(uint, String)> {
    connection.query_row(
        "SELECT file_id, name FROM alias WHERE id = $1;",
        &[&(alias_id as i64)],
        |row| row.get::<Option<i64>>(0).map(|id| (id as uint, row.get::<String>(1)))
    )
}

fn get_file_block_list(connection: &SqliteConnection, file_id: uint) -> SqliteResult<Box<[uint]>> {
    let mut statement = try!(connection.prepare("SELECT id FROM fileblock WHERE file_id = $1;"));
    let mut blocks = try!(statement.query(&[&(file_id as i64)]));
    let mut block_id_list = Vec::new();

    for block in blocks {
        block_id_list.push(try!(block).get::<i64>(0) as uint);
    }

    Ok(block_id_list.into_boxed_slice())
}

pub fn persist_file(connection: &SqliteConnection, directory: Directory, filename: &str, hash: &str, block_id_list: &[uint]) -> SqliteResult<()> {
    let transaction = try!(connection.transaction());

    try!(connection.execute("INSERT INTO file (hash) VALUES ($1);", &[&hash]));

    let file_id = connection.last_insert_rowid();

    for (ordinal, block_id) in block_id_list.iter().enumerate() {
        try!(connection.execute(
            "INSERT INTO fileblock (file_id, block_id, ordinal) VALUES ($1, $2, $3);"
            , &[&(file_id as i64), &(*block_id as i64), &(ordinal as i64)]
        ));
    }

    let alias_query = "INSERT INTO alias (directory_id, file_id, name, timestamp) VALUES ($1, $2, $3, $4);";
    let timestamp = time::get_time().sec;
    let directory_id: Option<i64> = match directory {
        Directory::Root      => None,
        Directory::Child(id) => Some(id as i64)
    };

    try!(connection.execute(alias_query, &[&directory_id, &(file_id as i64), &filename, &(timestamp as i64)]));

    transaction.commit()
}

pub fn persist_block(connection: &SqliteConnection, hash: &str) -> SqliteResult<uint> {
    connection.execute("INSERT INTO block (hash) VALUES ($1);", &[&hash])
}

pub fn file_known(connection: &SqliteConnection, hash: &str) -> bool {
    connection.query_row(
        "SELECT COUNT(id) FROM file WHERE hash = $1;",
        &[&hash],
        |row| row.get::<i64>(0) > 0
    )
}

pub fn block_hash_from_id(connection: &SqliteConnection, id: uint) -> String {
    connection.query_row(
        "SELECT hash FROM block WHERE id = $1;",
        &[&(id as i64)],
        |row| row.get(0)
    )
}

pub fn block_id_from_hash(connection: &SqliteConnection, hash: &str) -> Option<uint> {
    let result: Option<i64> = connection.query_row(
        "SELECT SUM(id) FROM block WHERE hash = $1;",
        &[&hash],
        |row| row.get(0)
    );

    result.map(|signed| signed as uint)
}

pub fn get_directory(connection: &SqliteConnection, parent: Directory, name: &str) -> SqliteResult<Directory> {
    let parent_id: Option<i64> = match parent {
        Directory::Root      => None,
        Directory::Child(id) => Some(id as i64)
    };

    // TODO: escape name!
    let select_query = "SELECT SUM(id) FROM directory WHERE name = $1 AND parent_id = $2;";
    let directory_id: Option<i64> = connection.query_row(select_query, &[&name, &parent_id], |row| row.get(0));

    if directory_id.is_some() {
        return Ok(Directory::Child(directory_id.unwrap() as uint));
    }

    let insert_query = "INSERT INTO directory (parent_id, name) VALUES ($1, $2);";

    connection.execute(insert_query, &[&parent_id, &name])
        .map(|_| Directory::Child(connection.last_insert_rowid() as uint))
}

pub fn set_key(connection: &SqliteConnection, key: &str, value: &str) -> SqliteResult<uint> {
    connection.execute("INSERT INTO setting (key, value) VALUES ($1, $2);", &[&key, &value])
}

pub fn get_key(connection: &SqliteConnection, key: &str) -> Option<String> {
    connection.query_row(
        "SELECT value FROM setting WHERE key = $1;",
        &[&key],
        |row| row.get(0)
    )
}

pub fn setup(connection: &SqliteConnection) -> SqliteResult<()> {
    let queries = [
        "CREATE TABLE directory (
            id        INTEGER PRIMARY KEY,
            parent_id INTEGER,
            name      TEXT NOT NULL,
            FOREIGN KEY(parent_id) REFERENCES directory(id),
            UNIQUE(parent_id, name)
        );",
        "CREATE TABLE file (
            id           INTEGER PRIMARY KEY,
            hash         TEXT NOT NULL
        );",
        "CREATE TABLE alias (
            id           INTEGER PRIMARY KEY,
            directory_id INTEGER,
            file_id      INTEGER,
            name         TEXT NOT NULL,
            timestamp    INTEGER,
            FOREIGN KEY(directory_id) REFERENCES directory(id),
            FOREIGN KEY(file_id) REFERENCES file(id)
        );",
        "CREATE TABLE block (
            id           INTEGER PRIMARY KEY,
            hash         TEXT NOT NULL
        );",
        "CREATE TABLE fileblock (
            id           INTEGER PRIMARY KEY,
            file_id      INTEGER NOT NULL,
            ordinal      INTEGER NOT NULL,
            block_id     INTEGER NOT NULL,
            FOREIGN KEY(file_id) REFERENCES file(id),
            FOREIGN KEY(block_id) REFERENCES block(id)
        );",
        "CREATE TABLE setting (
            key          TEXT PRIMARY KEY,
            value        TEXT
        );"
    ];

    for query in queries.iter() {
        try!(connection.execute(*query, &[]));
    }

    Ok(())
}
<|endoftext|>


<commit_before>#![allow(clippy::toplevel_ref_arg)]

use proc_macro::TokenStream;
use std::io::Write;
use std::process;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

const DEBUG_NOTE: &str = ":  use --release
  Syn's test suite has some tests that run on every source file
  and test case in the rust-lang/rust repo, which can be pretty
  slow in debug mode. Consider running cargo test with `--release`
  to speed things up.";

const FEATURES_ERROR: &str = ":  use --all-features
  Syn's test suite normally only works with all-features enabled.
  Run again with `--all-features`, or run with `--features test`
  to bypass this check.";

#[proc_macro]
pub fn check(_input: TokenStream) -> TokenStream {
    let ref mut stderr = StandardStream::stderr(ColorChoice::Auto);

    if cfg!(debug_assertions) {
        let yellow = ColorSpec::new().set_fg(Some(Color::Yellow)).clone();
        _ = writeln!(stderr);
        _ = stderr.set_color(yellow.clone().set_bold(true));
        _ = write!(stderr, "NOTE");
        for line in DEBUG_NOTE.lines() {
            _ = stderr.set_color(&yellow);
            _ = writeln!(stderr, "{}", line);
        }
        _ = stderr.reset();
    }

    if cfg!(not(feature = "all-features")) {
        let red = ColorSpec::new().set_fg(Some(Color::Red)).clone();
        _ = writeln!(stderr);
        _ = stderr.set_color(red.clone().set_bold(true));
        _ = write!(stderr, "ERROR");
        for line in FEATURES_ERROR.lines() {
            _ = stderr.set_color(&red);
            _ = writeln!(stderr, "{}", line);
        }
        _ = stderr.reset();
        process::exit(1);
    }

    TokenStream::new()
}
<commit_msg>Suppress "emit":"dep-info" json from syn-test-suite-feature-check<commit_after>#![allow(clippy::toplevel_ref_arg)]

use proc_macro::TokenStream;
use std::io::Write;
use std::process;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

const DEBUG_NOTE: &str = ":  use --release
  Syn's test suite has some tests that run on every source file
  and test case in the rust-lang/rust repo, which can be pretty
  slow in debug mode. Consider running cargo test with `--release`
  to speed things up.";

const FEATURES_ERROR: &str = ":  use --all-features
  Syn's test suite normally only works with all-features enabled.
  Run again with `--all-features`, or run with `--features test`
  to bypass this check.";

#[proc_macro]
pub fn check(_input: TokenStream) -> TokenStream {
    let ref mut stderr = StandardStream::stderr(ColorChoice::Auto);
    let mut needs_newline = true;

    if cfg!(debug_assertions) {
        let yellow = ColorSpec::new().set_fg(Some(Color::Yellow)).clone();
        _ = writeln!(stderr);
        _ = stderr.set_color(yellow.clone().set_bold(true));
        _ = write!(stderr, "NOTE");
        for line in DEBUG_NOTE.lines() {
            _ = stderr.set_color(&yellow);
            _ = writeln!(stderr, "{}", line);
        }
        _ = stderr.reset();
        _ = writeln!(stderr);
        needs_newline = false;
    }

    if cfg!(not(feature = "all-features")) {
        let red = ColorSpec::new().set_fg(Some(Color::Red)).clone();
        if needs_newline {
            _ = writeln!(stderr);
        }
        _ = stderr.set_color(red.clone().set_bold(true));
        _ = write!(stderr, "ERROR");
        for line in FEATURES_ERROR.lines() {
            _ = stderr.set_color(&red);
            _ = writeln!(stderr, "{}", line);
        }
        _ = stderr.reset();
        _ = writeln!(stderr);
        process::exit(1);
    }

    TokenStream::new()
}
<|endoftext|>


<commit_before>/// ## Internal Code Guide
///
/// This crate is centered around two traits, Parse and Generate,
/// which define how to parse a struct from a Parser over Rust code
/// and how to generate Rust code from a struct respectively.
///
/// All the parsing and generation of code is done through these traits.
///
/// `describe` is responsible for expanding the `describe!` macro
/// as a whole, and delegates to the Parse and Generate implementations
/// for `DescribeState`, which holds all the information necessary to
/// generate the AST for an expanded `describe!`.
///
/// The Parse implementation of DescribeState delegates to the Parse
/// implementation of Test, defined in the test module, and Bench,
/// defined in the bench module. The Generate implementation does the
/// same.
///
/// Most of the code can be understood by just walking through the
/// implementations of Parse and Generate for all the types inside
/// this crate, which can be found in the parse and generate
/// modules respectively.
///

use syntax::{ast, codemap, parse};
use syntax::ptr::P;
use syntax::ext::base;

use parse::Parse;
use generate::Generate;
use test::Test;
use bench::Bench;

/// Defines the state of a `describe!` macro as it is parsing.
#[derive(Clone)]
pub struct DescribeState {
    pub name: Option<ast::Ident>,
    pub before: Option<P<ast::Block>>,
    pub after: Option<P<ast::Block>>,
    pub before_each: Option<P<ast::Block>>,
    pub after_each: Option<P<ast::Block>>,
    pub subblocks: Vec<SubBlock>
}

/// Any supported subblock.
#[derive(Clone)]
pub enum SubBlock {
    Test(Test),
    Bench(Bench),
    Describe(DescribeState)
}

/// Defines the overarching `describe!` syntax extension.
///
/// All other macros in stainless are actually "fake" in the sense
/// that they are detected and expanded inside of the implementation
/// of `describe!`.
pub fn describe<'a>(cx: &'a mut base::ExtCtxt, sp: codemap::Span,
                name: ast::Ident, tokens: Vec<ast::TokenTree>) -> Box<base::MacResult + 'a> {
    // Parse a full DescribeState from the input, emitting errors if used incorrectly.
    let state: DescribeState = Parse::parse(&mut parse::tts_to_parser(cx.parse_sess(), tokens, cx.cfg()), (sp, &mut*cx, Some(name)));

    // Export the new module.
    base::MacItems::new(Some(state.generate(sp, cx, None)).into_iter())
}

<commit_msg>Fix for `rustc 1.0.0-dev (1cc8b6ec6 2015-03-02) (built 2015-03-02)`.<commit_after>/// ## Internal Code Guide
///
/// This crate is centered around two traits, Parse and Generate,
/// which define how to parse a struct from a Parser over Rust code
/// and how to generate Rust code from a struct respectively.
///
/// All the parsing and generation of code is done through these traits.
///
/// `describe` is responsible for expanding the `describe!` macro
/// as a whole, and delegates to the Parse and Generate implementations
/// for `DescribeState`, which holds all the information necessary to
/// generate the AST for an expanded `describe!`.
///
/// The Parse implementation of DescribeState delegates to the Parse
/// implementation of Test, defined in the test module, and Bench,
/// defined in the bench module. The Generate implementation does the
/// same.
///
/// Most of the code can be understood by just walking through the
/// implementations of Parse and Generate for all the types inside
/// this crate, which can be found in the parse and generate
/// modules respectively.
///

use syntax::{ast, codemap, parse};
use syntax::ptr::P;
use syntax::ext::base;
use syntax::util::small_vector::SmallVector;

use parse::Parse;
use generate::Generate;
use test::Test;
use bench::Bench;

/// Defines the state of a `describe!` macro as it is parsing.
#[derive(Clone)]
pub struct DescribeState {
    pub name: Option<ast::Ident>,
    pub before: Option<P<ast::Block>>,
    pub after: Option<P<ast::Block>>,
    pub before_each: Option<P<ast::Block>>,
    pub after_each: Option<P<ast::Block>>,
    pub subblocks: Vec<SubBlock>
}

/// Any supported subblock.
#[derive(Clone)]
pub enum SubBlock {
    Test(Test),
    Bench(Bench),
    Describe(DescribeState)
}

/// Defines the overarching `describe!` syntax extension.
///
/// All other macros in stainless are actually "fake" in the sense
/// that they are detected and expanded inside of the implementation
/// of `describe!`.
pub fn describe<'a>(cx: &'a mut base::ExtCtxt, sp: codemap::Span,
                name: ast::Ident, tokens: Vec<ast::TokenTree>) -> Box<base::MacResult + 'a> {
    // Parse a full DescribeState from the input, emitting errors if used incorrectly.
    let state: DescribeState = Parse::parse(&mut parse::tts_to_parser(cx.parse_sess(), tokens, cx.cfg()), (sp, &mut*cx, Some(name)));

    // Export the new module.
    base::MacEager::items(SmallVector::one(state.generate(sp, cx, None)))
}

<|endoftext|>


<commit_before>use cargo_helper::CargoInfo;
use Bytes;
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use pbr::{MultiBar, Pipe, ProgressBar, Units};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::thread;
use std::time::{Instant, Duration};
use write::{OutputFileWriter, OutputChunkWriter};

/// Constant to represent the length of the buffer to download
/// the remote content
const DOWNLOAD_BUFFER_BYTES: usize = 1024 * 64;

/// Constant to represent the refresh interval (in milliseconds)
/// for the CLI
const PROGRESS_UPDATE_INTERVAL_MILLIS: u64 = 500;

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

macro_rules! initbar {
    ($mp:ident,$mpb:ident,$length:expr,$index:expr) => {
        let mut $mp = $mpb.create_bar($length);
        $mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        $mp.format("|#--|");
        $mp.show_tick = true;
        $mp.show_speed = true;
        $mp.show_percent = true;
        $mp.show_counter = false;
        $mp.show_time_left = true;
        $mp.set_units(Units::Bytes);
        $mp.message(&format!("Chunk {} ", $index));
    }
}

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, RangeBytes)> {

    get_chunk_length(chunk_index, content_length, global_chunk_length).map(|range| {
        let mut header = Headers::new();
        header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
        (header, RangeBytes(range.0, range.1 - range.0))
    })
}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    mut chunk_writer: OutputChunkWriter,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>,
                    monothreading: bool)
                    -> Result<Bytes, Error> {

    let mut body = http_client
        .get_http_response_using_headers(url, http_header)
        .unwrap();
    if monothreading && !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; DOWNLOAD_BUFFER_BYTES];
    let mut sum_bytes = 0;

    let progress_update_interval = Duration::from_millis(PROGRESS_UPDATE_INTERVAL_MILLIS);
    let mut last_progress_bytes = 0;
    let mut last_progress_time = Instant::now() - progress_update_interval;

    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }

        chunk_writer.write(sum_bytes, &bytes_buffer[0..n]);

        sum_bytes += n as u64;

        // Update the CLI
        if Instant::now().duration_since(last_progress_time) > progress_update_interval {
            last_progress_time = Instant::now();
            let progress_bytes_delta = sum_bytes - last_progress_bytes;
            last_progress_bytes = sum_bytes;
            mpb.add(progress_bytes_delta);
        }
    }
    mpb.add(sum_bytes - last_progress_bytes);
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server,
/// * a custom authorization to access and download the remote content.
pub fn download_chunks(cargo_info: CargoInfo,
                       mut out_file: OutputFileWriter,
                       nb_chunks: u64,
                       url: &str)
                       -> bool {
    let (content_length, auth_header_factory) = (cargo_info.content_length, cargo_info.auth_header);

    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunks: ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let (mut http_header, RangeBytes(chunk_offset, chunk_length)) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let hyper_client = Client::new();
        let url_clone = String::from(url);
        if let Some(auth_header_factory) = auth_header_factory.clone() {
            http_header.set(auth_header_factory.build_header());
        }
        let monothreading = cargo_info.accept_partialcontent;

        // TODO : Download from mirrors ( %i mod m_list )

        // Initialize the progress bar for that chunk
        initbar!(mp, mpb, chunk_length, chunk_index);

        let chunk_writer = out_file.get_chunk_writer(chunk_offset);

        // In this work, we push a boolean value to know if the chunk is OK
        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                               http_header,
                                                               chunk_writer,
                                                               &url_clone,
                                                               &mut mp,
                                                               monothreading) {
                                    Ok(bytes_written) => {
            mp.finish();
            if bytes_written == 0 {
                error!(&format!("The downloaded chunk {} is empty", chunk_index));
            }
            return true;
        }
                                    Err(error) => {
            mp.finish();
            error!(&format!("Cannot download the chunk {}, due to error {}",
                            chunk_index,
                            error));
            return false;
        }
                                }));
    }

    mpb.listen();

    // Contain the result state for chunks
    let mut child_results: Vec<bool> = Vec::with_capacity(nb_chunks as usize);

    for child in jobs {
        match child.join() {
            Ok(b) => child_results.push(b),
            Err(_) => child_results.push(false),
        }
    }

    // Check if all chunks are OK
    return child_results.iter().all(|x| *x);
}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::{get_header_from_index, RangeBytes};
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, RangeBytes(750, 247))),
                   get_header_from_index(3, 998, 250));
    }

}
<commit_msg>Improve downloads using mirrors<commit_after>use cargo_helper::CargoInfo;
use Bytes;
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use MirrorsList;
use pbr::{MultiBar, Pipe, ProgressBar, Units};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::path::Path;
use std::thread;
use std::time::{Instant, Duration};
use write::{OutputFileWriter, OutputChunkWriter};

/// Constant to represent the length of the buffer to download
/// the remote content
const DOWNLOAD_BUFFER_BYTES: usize = 1024 * 64;

/// Constant to represent the refresh interval (in milliseconds)
/// for the CLI
const PROGRESS_UPDATE_INTERVAL_MILLIS: u64 = 500;

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

macro_rules! initbar {
    ($mp:ident,$mpb:ident,$length:expr,$index:expr,$server:expr) => {
        let mut $mp = $mpb.create_bar($length);
        $mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        $mp.format("|#--|");
        $mp.show_tick = true;
        $mp.show_speed = true;
        $mp.show_percent = true;
        $mp.show_counter = false;
        $mp.show_time_left = true;
        $mp.set_units(Units::Bytes);
        $mp.message(&format!("Chunk {} (from {}) ", $index, $server));
    }
}

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, RangeBytes)> {

    get_chunk_length(chunk_index, content_length, global_chunk_length).map(|range| {
        let mut header = Headers::new();
        header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
        (header, RangeBytes(range.0, range.1 - range.0))
    })
}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    mut chunk_writer: OutputChunkWriter,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>,
                    monothreading: bool)
                    -> Result<Bytes, Error> {

    let mut body = http_client
        .get_http_response_using_headers(url, http_header)
        .unwrap();
    if monothreading && !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; DOWNLOAD_BUFFER_BYTES];
    let mut sum_bytes = 0;

    let progress_update_interval = Duration::from_millis(PROGRESS_UPDATE_INTERVAL_MILLIS);
    let mut last_progress_bytes = 0;
    let mut last_progress_time = Instant::now() - progress_update_interval;

    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }

        chunk_writer.write(sum_bytes, &bytes_buffer[0..n]);

        sum_bytes += n as u64;

        // Update the CLI
        if Instant::now().duration_since(last_progress_time) > progress_update_interval {
            last_progress_time = Instant::now();
            let progress_bytes_delta = sum_bytes - last_progress_bytes;
            last_progress_bytes = sum_bytes;
            mpb.add(progress_bytes_delta);
        }
    }
    mpb.add(sum_bytes - last_progress_bytes);
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server,
/// * a custom authorization to access and download the remote content.
pub fn download_chunks<'a>(cargo_info: CargoInfo<'a>,
                           mut out_file: OutputFileWriter,
                           nb_chunks: u64,
                           filename: &str)
                           -> bool {
    let (content_length, auth_header_factory) = (cargo_info.content_length, cargo_info.auth_header);
    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;

    let mirrors: MirrorsList<'a> = cargo_info.best_mirrors;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunk(s): ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let server_url = mirrors[chunk_index as usize % mirrors.len()];
        let path_url = Path::new(server_url).join(filename);
        let url_clone = String::from(path_url.to_str().unwrap());
        let (mut http_header, RangeBytes(chunk_offset, chunk_length)) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let hyper_client = Client::new();
        if let Some(auth_header_factory) = auth_header_factory.clone() {
            http_header.set(auth_header_factory.build_header());
        }
        let monothreading = cargo_info.accept_partialcontent;

        // Initialize the progress bar for that chunk
        initbar!(mp, mpb, chunk_length, chunk_index, server_url);

        let chunk_writer = out_file.get_chunk_writer(chunk_offset);

        // In this work, we push a boolean value to know if the chunk is OK
        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                               http_header,
                                                               chunk_writer,
                                                               &url_clone,
                                                               &mut mp,
                                                               monothreading) {
                                    Ok(bytes_written) => {
            mp.finish();
            if bytes_written == 0 {
                error!(&format!("The downloaded chunk {} is empty", chunk_index));
            }
            return true;
        }
                                    Err(error) => {
            mp.finish();
            error!(&format!("Cannot download the chunk {}, due to error {}",
                            chunk_index,
                            error));
            return false;
        }
                                }));
    }

    mpb.listen();

    // Contain the result state for chunks
    let mut child_results: Vec<bool> = Vec::with_capacity(nb_chunks as usize);

    for child in jobs {
        match child.join() {
            Ok(b) => child_results.push(b),
            Err(_) => child_results.push(false),
        }
    }

    // Check if all chunks are OK
    return child_results.iter().all(|x| *x);
}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::{get_header_from_index, RangeBytes};
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, RangeBytes(750, 247))),
                   get_header_from_index(3, 998, 250));
    }

}
<|endoftext|>


<commit_before>#![cfg(test)]
use core;
extern crate alloc_no_stdlib;
use enc::util::brotli_min_size_t;
use super::super::alloc::{AllocatedStackMemory, Allocator, SliceWrapper, SliceWrapperMut, StackAllocator, bzero};
use super::encode::{BrotliEncoderCreateInstance,
                    BrotliEncoderSetParameter,
                    BrotliEncoderDestroyInstance,
                    BrotliEncoderIsFinished,
                    BrotliEncoderCompressStream,
                    BrotliEncoderParameter,
                    BrotliEncoderOperation};
use super::histogram::{ContextType, HistogramLiteral, HistogramCommand, HistogramDistance};
use super::cluster::{HistogramPair};
#[cfg(not(feature="no-stdlib"))]
use std::vec::Vec;
#[cfg(not(feature="no-stdlib"))]
use std::io;
extern {
  fn calloc(n_elem : usize, el_size : usize) -> *mut u8;
}
extern {
  fn free(ptr : *mut u8);
}

use core::ops;
use super::entropy_encode::{HuffmanTree};
use super::command::{Command};
pub use super::super::{BrotliDecompressStream, BrotliResult, BrotliState, HuffmanCode};

declare_stack_allocator_struct!(MemPool, 4096, stack);
declare_stack_allocator_struct!(CallocatedFreelist4096, 4096, calloc);
declare_stack_allocator_struct!(CallocatedFreelist2048, 2048, calloc);

fn oneshot_compress(input: &[u8], mut output: &mut [u8], quality : u32, lgwin : u32) -> (i32, usize) {
  let mut stack_u8_buffer = unsafe{define_allocator_memory_pool!(4096, u8, [0; 1024 * 1024], calloc)};
  let mut stack_u16_buffer = unsafe{define_allocator_memory_pool!(4096, u16, [0; 128 * 1024], calloc)};
  let mut stack_i32_buffer = unsafe{define_allocator_memory_pool!(4096, i32, [0; 128 * 1024], calloc)};
  let mut stack_u32_buffer = unsafe{define_allocator_memory_pool!(4096, u32, [0; 32 * 1024 * 1024], calloc)};
  let mut stack_f64_buffer = unsafe{define_allocator_memory_pool!(2048, f64, [0; 128 * 1024], calloc)};
  let mut stack_hl_buffer = unsafe{define_allocator_memory_pool!(2048, HistogramLiteral, [0; 128 * 1024], calloc)};
  let mut stack_hc_buffer = unsafe{define_allocator_memory_pool!(2048, HistogramCommand, [0; 128 * 1024], calloc)};
  let mut stack_hd_buffer = unsafe{define_allocator_memory_pool!(2048, HistogramDistance, [0; 128 * 1024], calloc)};
  let mut stack_hp_buffer = unsafe{define_allocator_memory_pool!(2048, HistogramPair, [0; 128 * 1024], calloc)};
  let mut stack_ct_buffer = unsafe{define_allocator_memory_pool!(2048, ContextType, [0; 128 * 1024], calloc)};
  let mut stack_ht_buffer = unsafe{define_allocator_memory_pool!(2048, HuffmanTree, [0; 128 * 1024], calloc)};
  let mut stack_mc_buffer = unsafe{define_allocator_memory_pool!(2048, Command, [0; 128 * 1024], calloc)};
  let stack_u8_allocator = CallocatedFreelist4096::<u8>::new_allocator(stack_u8_buffer.data, bzero);
  let stack_u16_allocator = CallocatedFreelist4096::<u16>::new_allocator(stack_u16_buffer.data, bzero);
  let stack_i32_allocator = CallocatedFreelist4096::<i32>::new_allocator(stack_i32_buffer.data, bzero);
  let stack_u32_allocator = CallocatedFreelist4096::<u32>::new_allocator(stack_u32_buffer.data, bzero);
  let mut mf64 = CallocatedFreelist2048::<f64>::new_allocator(stack_f64_buffer.data, bzero);
  let stack_mc_allocator = CallocatedFreelist2048::<Command>::new_allocator(stack_mc_buffer.data, bzero);
  let mut mhl = CallocatedFreelist2048::<HistogramLiteral>::new_allocator(stack_hl_buffer.data, bzero);
  let mut mhc = CallocatedFreelist2048::<HistogramCommand>::new_allocator(stack_hc_buffer.data, bzero);
  let mut mhd = CallocatedFreelist2048::<HistogramDistance>::new_allocator(stack_hd_buffer.data, bzero);
  let mut mhp = CallocatedFreelist2048::<HistogramPair>::new_allocator(stack_hp_buffer.data, bzero);
  let mut mct = CallocatedFreelist2048::<ContextType>::new_allocator(stack_ct_buffer.data, bzero);
  let mut mht = CallocatedFreelist2048::<HuffmanTree>::new_allocator(stack_ht_buffer.data, bzero);
  let mut s_orig = BrotliEncoderCreateInstance(stack_u8_allocator,
                                               stack_u16_allocator,
                                               stack_i32_allocator,
                                               stack_u32_allocator,
                                               stack_mc_allocator);
  let mut next_in_offset: usize = 0;
  let mut next_out_offset: usize = 0;
  {
      let mut s = &mut s_orig;

      BrotliEncoderSetParameter(s,
                                BrotliEncoderParameter::BROTLI_PARAM_QUALITY,
                                quality as (u32));
      BrotliEncoderSetParameter(s,
                                BrotliEncoderParameter::BROTLI_PARAM_LGWIN,
                                lgwin as (u32));
      BrotliEncoderSetParameter(s, BrotliEncoderParameter::BROTLI_PARAM_MODE, 0 as (u32)); // gen, text, font
      BrotliEncoderSetParameter(s,
                                BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
                                input.len() as (u32));
      while true {
          const in_batch_size : usize = 3;
          const out_batch_size : usize = 2;
          let mut available_in: usize = brotli_min_size_t(input.len() - next_in_offset, in_batch_size);
          let mut available_out: usize = brotli_min_size_t(output.len() - next_out_offset, out_batch_size);
          if available_out == 0 {
              panic!("No output buffer space");
          }
          let mut total_out = Some(0usize);
          let op : BrotliEncoderOperation;
          if available_in == input.len() - next_in_offset {
              op = BrotliEncoderOperation::BROTLI_OPERATION_FINISH;
          } else {
              op = BrotliEncoderOperation::BROTLI_OPERATION_PROCESS;
          }
          let result = BrotliEncoderCompressStream(s,
                                               &mut mf64, &mut mhl, &mut mhc, &mut mhd, &mut mhp, &mut mct, &mut mht,
                                               op,
                                               &mut available_in,
                                               input,
                                               &mut next_in_offset,
                                               &mut available_out,
                                               output,
                                               &mut next_out_offset,
                                               &mut total_out);
          if result <= 0 {
              return (result, next_out_offset);
          }
          if BrotliEncoderIsFinished(s) != 0 {
              break
          }
      }

      BrotliEncoderDestroyInstance(s);
  }

  return (1, next_out_offset);
}

fn oneshot_decompress(mut compressed: &mut [u8], mut output: &mut [u8]) -> (BrotliResult, usize, usize) {
  let mut available_in: usize = compressed.len();
  let mut available_out: usize = output.len();
  let mut stack_u8_buffer = define_allocator_memory_pool!(4096, u8, [0; 100 * 1024], stack);
  let mut stack_u32_buffer = define_allocator_memory_pool!(4096, u32, [0; 12 * 1024], stack);
  let mut stack_hc_buffer = define_allocator_memory_pool!(4096,
                                                          HuffmanCode,
                                                          [HuffmanCode::default(); 18 * 1024],
                                                          stack);

  let stack_u8_allocator = MemPool::<u8>::new_allocator(&mut stack_u8_buffer, bzero);
  let stack_u32_allocator = MemPool::<u32>::new_allocator(&mut stack_u32_buffer, bzero);
  let stack_hc_allocator = MemPool::<HuffmanCode>::new_allocator(&mut stack_hc_buffer, bzero);
  let mut input_offset: usize = 0;
  let mut output_offset: usize = 0;
  let mut written: usize = 0;
  let mut brotli_state =
    BrotliState::new(stack_u8_allocator, stack_u32_allocator, stack_hc_allocator);
  let result = BrotliDecompressStream(&mut available_in,
                                      &mut input_offset,
                                      &compressed[..],
                                      &mut available_out,
                                      &mut output_offset,
                                      &mut output,
                                      &mut written,
                                      &mut brotli_state);
  brotli_state.BrotliStateCleanup();
  return (result, input_offset, output_offset);

}

fn oneshot(input: &mut [u8], mut compressed: &mut [u8], mut output: &mut [u8]) -> (BrotliResult, usize, usize) {
  let (success, mut available_in) = oneshot_compress(input, compressed, 9, 4);
  if success == 0 {
      //return (BrotliResult::ResultFailure, 0, 0);
      available_in = compressed.len();
  }
  return oneshot_decompress(&mut compressed[..available_in], output);
}

#[test]
fn test_roundtrip_10x10y() {
  const BUFFER_SIZE: usize = 16384;
  let mut compressed: [u8; 13] = [0;13];
  let mut output = [0u8; BUFFER_SIZE];
    let mut input  = ['x' as u8, 'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8,
                      'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8,
                      'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8,
                      'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8];
  let (result, compressed_offset, output_offset) = oneshot(&mut input[..], &mut compressed, &mut output[..]);
  match result {
    BrotliResult::ResultSuccess => {}
    _ => assert!(false),
  }
  let mut i: usize = 0;
  while i < 10 {
    assert_eq!(output[i], 'x' as u8);
    assert_eq!(output[i + 10], 'y' as u8);
    i += 1;
  }
  assert_eq!(output_offset, 20);
  assert_eq!(compressed_offset, compressed.len());
}

#[test]
fn test_roundtrip_x() {
  const BUFFER_SIZE: usize = 16384;
  let mut compressed: [u8; 6] = [0x0b, 0x00, 0x80, 0x58, 0x03, 0];
  let mut output = [0u8; BUFFER_SIZE];
  let mut input = ['X' as u8];
  let (result, compressed_offset, output_offset) = oneshot(&mut input[..], &mut compressed[..], &mut output[..]);
  match result {
    BrotliResult::ResultSuccess => {}
    _ => assert!(false),
  }
  assert_eq!(output[0], 'X' as u8);
  assert_eq!(output_offset, 1);
  assert_eq!(compressed_offset, compressed.len());
}

#[test]
fn test_roundtrip_empty() {
  const BUFFER_SIZE: usize = 16384;
  let mut compressed: [u8; 2] = [0x06,0];
  let mut output = [0u8; 1];
  let (result, compressed_offset, output_offset) = oneshot(&mut [], &mut compressed[..], &mut output[..]);
  match result {
    BrotliResult::ResultSuccess => {}
    _ => assert!(false),
  }
  assert_eq!(output_offset, 0);
  assert_eq!(compressed_offset, compressed.len());
}
const QF_BUFFER_SIZE: usize = 180 * 1024;
static mut quick_fox_output: [u8; QF_BUFFER_SIZE] = [0u8; QF_BUFFER_SIZE];

#[cfg(not(feature="no-stdlib"))]
struct Buffer {
  data: Vec<u8>,
  read_offset: usize,
}
#[cfg(not(feature="no-stdlib"))]
impl Buffer {
  pub fn new(buf: &[u8]) -> Buffer {
    let mut ret = Buffer {
      data: Vec::<u8>::new(),
      read_offset: 0,
    };
    ret.data.extend(buf);
    return ret;
  }
}
#[cfg(not(feature="no-stdlib"))]
impl io::Read for Buffer {
  fn read(self: &mut Self, buf: &mut [u8]) -> io::Result<usize> {
    let bytes_to_read = ::core::cmp::min(buf.len(), self.data.len() - self.read_offset);
    if bytes_to_read > 0 {
      buf[0..bytes_to_read]
        .clone_from_slice(&self.data[self.read_offset..self.read_offset + bytes_to_read]);
    }
    self.read_offset += bytes_to_read;
    return Ok(bytes_to_read);
  }
}
#[cfg(not(feature="no-stdlib"))]
impl io::Write for Buffer {
  fn write(self: &mut Self, buf: &[u8]) -> io::Result<usize> {
    self.data.extend(buf);
    return Ok(buf.len());
  }
  fn flush(self: &mut Self) -> io::Result<()> {
    return Ok(());
  }
}

<commit_msg>added a generic test function so we can pass in arbitrary files to test<commit_after>#![cfg(test)]
use core;
extern crate alloc_no_stdlib;
use enc::util::brotli_min_size_t;
use super::super::alloc::{AllocatedStackMemory, Allocator, SliceWrapper, SliceWrapperMut, StackAllocator, bzero};
use super::encode::{BrotliEncoderCreateInstance,
                    BrotliEncoderSetParameter,
                    BrotliEncoderDestroyInstance,
                    BrotliEncoderIsFinished,
                    BrotliEncoderCompressStream,
                    BrotliEncoderParameter,
                    BrotliEncoderOperation};
use super::histogram::{ContextType, HistogramLiteral, HistogramCommand, HistogramDistance};
use super::cluster::{HistogramPair};
#[cfg(not(feature="no-stdlib"))]
use std::vec::Vec;
#[cfg(not(feature="no-stdlib"))]
use std::io;
extern {
  fn calloc(n_elem : usize, el_size : usize) -> *mut u8;
}
extern {
  fn free(ptr : *mut u8);
}

use core::ops;
use super::entropy_encode::{HuffmanTree};
use super::command::{Command};
pub use super::super::{BrotliDecompressStream, BrotliResult, BrotliState, HuffmanCode};

declare_stack_allocator_struct!(MemPool, 4096, stack);
declare_stack_allocator_struct!(CallocatedFreelist4096, 4096, calloc);
declare_stack_allocator_struct!(CallocatedFreelist2048, 2048, calloc);

fn oneshot_compress(input: &[u8], mut output: &mut [u8], quality : u32, lgwin : u32) -> (i32, usize) {
  let mut stack_u8_buffer = unsafe{define_allocator_memory_pool!(4096, u8, [0; 1024 * 1024], calloc)};
  let mut stack_u16_buffer = unsafe{define_allocator_memory_pool!(4096, u16, [0; 128 * 1024], calloc)};
  let mut stack_i32_buffer = unsafe{define_allocator_memory_pool!(4096, i32, [0; 128 * 1024], calloc)};
  let mut stack_u32_buffer = unsafe{define_allocator_memory_pool!(4096, u32, [0; 32 * 1024 * 1024], calloc)};
  let mut stack_f64_buffer = unsafe{define_allocator_memory_pool!(2048, f64, [0; 128 * 1024], calloc)};
  let mut stack_hl_buffer = unsafe{define_allocator_memory_pool!(2048, HistogramLiteral, [0; 128 * 1024], calloc)};
  let mut stack_hc_buffer = unsafe{define_allocator_memory_pool!(2048, HistogramCommand, [0; 128 * 1024], calloc)};
  let mut stack_hd_buffer = unsafe{define_allocator_memory_pool!(2048, HistogramDistance, [0; 128 * 1024], calloc)};
  let mut stack_hp_buffer = unsafe{define_allocator_memory_pool!(2048, HistogramPair, [0; 128 * 1024], calloc)};
  let mut stack_ct_buffer = unsafe{define_allocator_memory_pool!(2048, ContextType, [0; 128 * 1024], calloc)};
  let mut stack_ht_buffer = unsafe{define_allocator_memory_pool!(2048, HuffmanTree, [0; 128 * 1024], calloc)};
  let mut stack_mc_buffer = unsafe{define_allocator_memory_pool!(2048, Command, [0; 128 * 1024], calloc)};
  let stack_u8_allocator = CallocatedFreelist4096::<u8>::new_allocator(stack_u8_buffer.data, bzero);
  let stack_u16_allocator = CallocatedFreelist4096::<u16>::new_allocator(stack_u16_buffer.data, bzero);
  let stack_i32_allocator = CallocatedFreelist4096::<i32>::new_allocator(stack_i32_buffer.data, bzero);
  let stack_u32_allocator = CallocatedFreelist4096::<u32>::new_allocator(stack_u32_buffer.data, bzero);
  let mut mf64 = CallocatedFreelist2048::<f64>::new_allocator(stack_f64_buffer.data, bzero);
  let stack_mc_allocator = CallocatedFreelist2048::<Command>::new_allocator(stack_mc_buffer.data, bzero);
  let mut mhl = CallocatedFreelist2048::<HistogramLiteral>::new_allocator(stack_hl_buffer.data, bzero);
  let mut mhc = CallocatedFreelist2048::<HistogramCommand>::new_allocator(stack_hc_buffer.data, bzero);
  let mut mhd = CallocatedFreelist2048::<HistogramDistance>::new_allocator(stack_hd_buffer.data, bzero);
  let mut mhp = CallocatedFreelist2048::<HistogramPair>::new_allocator(stack_hp_buffer.data, bzero);
  let mut mct = CallocatedFreelist2048::<ContextType>::new_allocator(stack_ct_buffer.data, bzero);
  let mut mht = CallocatedFreelist2048::<HuffmanTree>::new_allocator(stack_ht_buffer.data, bzero);
  let mut s_orig = BrotliEncoderCreateInstance(stack_u8_allocator,
                                               stack_u16_allocator,
                                               stack_i32_allocator,
                                               stack_u32_allocator,
                                               stack_mc_allocator);
  let mut next_in_offset: usize = 0;
  let mut next_out_offset: usize = 0;
  {
      let mut s = &mut s_orig;

      BrotliEncoderSetParameter(s,
                                BrotliEncoderParameter::BROTLI_PARAM_QUALITY,
                                quality as (u32));
      BrotliEncoderSetParameter(s,
                                BrotliEncoderParameter::BROTLI_PARAM_LGWIN,
                                lgwin as (u32));
      BrotliEncoderSetParameter(s, BrotliEncoderParameter::BROTLI_PARAM_MODE, 0 as (u32)); // gen, text, font
      BrotliEncoderSetParameter(s,
                                BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
                                input.len() as (u32));
      while true {
          const in_batch_size : usize = 3;
          const out_batch_size : usize = 2;
          let mut available_in: usize = brotli_min_size_t(input.len() - next_in_offset, in_batch_size);
          let mut available_out: usize = brotli_min_size_t(output.len() - next_out_offset, out_batch_size);
          if available_out == 0 {
              panic!("No output buffer space");
          }
          let mut total_out = Some(0usize);
          let op : BrotliEncoderOperation;
          if available_in == input.len() - next_in_offset {
              op = BrotliEncoderOperation::BROTLI_OPERATION_FINISH;
          } else {
              op = BrotliEncoderOperation::BROTLI_OPERATION_PROCESS;
          }
          let result = BrotliEncoderCompressStream(s,
                                               &mut mf64, &mut mhl, &mut mhc, &mut mhd, &mut mhp, &mut mct, &mut mht,
                                               op,
                                               &mut available_in,
                                               input,
                                               &mut next_in_offset,
                                               &mut available_out,
                                               output,
                                               &mut next_out_offset,
                                               &mut total_out);
          if result <= 0 {
              return (result, next_out_offset);
          }
          if BrotliEncoderIsFinished(s) != 0 {
              break
          }
      }

      BrotliEncoderDestroyInstance(s);
  }

  return (1, next_out_offset);
}

fn oneshot_decompress(mut compressed: &mut [u8], mut output: &mut [u8]) -> (BrotliResult, usize, usize) {
  let mut available_in: usize = compressed.len();
  let mut available_out: usize = output.len();
  let mut stack_u8_buffer = define_allocator_memory_pool!(4096, u8, [0; 100 * 1024], stack);
  let mut stack_u32_buffer = define_allocator_memory_pool!(4096, u32, [0; 12 * 1024], stack);
  let mut stack_hc_buffer = define_allocator_memory_pool!(4096,
                                                          HuffmanCode,
                                                          [HuffmanCode::default(); 18 * 1024],
                                                          stack);

  let stack_u8_allocator = MemPool::<u8>::new_allocator(&mut stack_u8_buffer, bzero);
  let stack_u32_allocator = MemPool::<u32>::new_allocator(&mut stack_u32_buffer, bzero);
  let stack_hc_allocator = MemPool::<HuffmanCode>::new_allocator(&mut stack_hc_buffer, bzero);
  let mut input_offset: usize = 0;
  let mut output_offset: usize = 0;
  let mut written: usize = 0;
  let mut brotli_state =
    BrotliState::new(stack_u8_allocator, stack_u32_allocator, stack_hc_allocator);
  let result = BrotliDecompressStream(&mut available_in,
                                      &mut input_offset,
                                      &compressed[..],
                                      &mut available_out,
                                      &mut output_offset,
                                      &mut output,
                                      &mut written,
                                      &mut brotli_state);
  brotli_state.BrotliStateCleanup();
  return (result, input_offset, output_offset);

}

fn oneshot(input: &[u8], mut compressed: &mut [u8], mut output: &mut [u8]) -> (BrotliResult, usize, usize) {
  let (success, mut available_in) = oneshot_compress(input, compressed, 9, 4);
  if success == 0 {
      //return (BrotliResult::ResultFailure, 0, 0);
      available_in = compressed.len();
  }
  return oneshot_decompress(&mut compressed[..available_in], output);
}

#[test]
fn test_roundtrip_10x10y() {
  const BUFFER_SIZE: usize = 16384;
  let mut compressed: [u8; 13] = [0;13];
  let mut output = [0u8; BUFFER_SIZE];
    let mut input  = ['x' as u8, 'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8,
                      'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8,
                      'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8,
                      'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8];
  let (result, compressed_offset, output_offset) = oneshot(&mut input[..], &mut compressed, &mut output[..]);
  match result {
    BrotliResult::ResultSuccess => {}
    _ => assert!(false),
  }
  let mut i: usize = 0;
  while i < 10 {
    assert_eq!(output[i], 'x' as u8);
    assert_eq!(output[i + 10], 'y' as u8);
    i += 1;
  }
  assert_eq!(output_offset, 20);
  assert_eq!(compressed_offset, compressed.len());
}

macro_rules! test_roundtrip_file {
  ($filename : expr, $bufsize: expr) => {{
    let mut stack_u8_buffer = unsafe{define_allocator_memory_pool!(4096, u8, [0; 16 * 1024 * 1024], calloc)};
    let mut stack_u8_allocator = CallocatedFreelist4096::<u8>::new_allocator(stack_u8_buffer.data, bzero);

    let mut compressed = stack_u8_allocator.alloc_cell($bufsize);
    let inp = include_bytes!($filename);
    let mut output = stack_u8_allocator.alloc_cell(inp.len() + 16);
    let (result, compressed_offset, output_offset) = oneshot(&inp[..],
                                                          compressed.slice_mut(),
                                                          output.slice_mut());
    match result {
      BrotliResult::ResultSuccess => {}
      _ => assert!(false),
    }
    let mut i: usize = 0;
    for i in 0..inp.len() {
      assert_eq!(inp[i], output[i]);
    }
    assert_eq!(output_offset, inp.len());
    stack_u8_allocator.free_cell(output);
    stack_u8_allocator.free_cell(compressed);
  }};
}

#[test]
fn test_roundtrip_64x() {
  test_roundtrip_file!("../bin/testdata/64x", 72);
}
/* FIXME: doesn't yet pass
#[test]
fn test_roundtrip_asyoulik() {
  test_roundtrip_file!("../bin/testdata/asyoulik.txt", 64384);
}
*/
#[test]
fn test_roundtrip_quickfox() {
  test_roundtrip_file!("../bin/testdata/quickfox", 256);
}

#[test]
fn test_roundtrip_x() {
  const BUFFER_SIZE: usize = 16384;
  let mut compressed: [u8; 6] = [0x0b, 0x00, 0x80, 0x58, 0x03, 0];
  let mut output = [0u8; BUFFER_SIZE];
  let mut input = ['X' as u8];
  let (result, compressed_offset, output_offset) = oneshot(&mut input[..], &mut compressed[..], &mut output[..]);
  match result {
    BrotliResult::ResultSuccess => {}
    _ => assert!(false),
  }
  assert_eq!(output[0], 'X' as u8);
  assert_eq!(output_offset, 1);
  assert_eq!(compressed_offset, compressed.len());
}

#[test]
fn test_roundtrip_empty() {
  const BUFFER_SIZE: usize = 16384;
  let mut compressed: [u8; 2] = [0x06,0];
  let mut output = [0u8; 1];
  let (result, compressed_offset, output_offset) = oneshot(&mut [], &mut compressed[..], &mut output[..]);
  match result {
    BrotliResult::ResultSuccess => {}
    _ => assert!(false),
  }
  assert_eq!(output_offset, 0);
  assert_eq!(compressed_offset, compressed.len());
}
const QF_BUFFER_SIZE: usize = 180 * 1024;
static mut quick_fox_output: [u8; QF_BUFFER_SIZE] = [0u8; QF_BUFFER_SIZE];

#[cfg(not(feature="no-stdlib"))]
struct Buffer {
  data: Vec<u8>,
  read_offset: usize,
}
#[cfg(not(feature="no-stdlib"))]
impl Buffer {
  pub fn new(buf: &[u8]) -> Buffer {
    let mut ret = Buffer {
      data: Vec::<u8>::new(),
      read_offset: 0,
    };
    ret.data.extend(buf);
    return ret;
  }
}
#[cfg(not(feature="no-stdlib"))]
impl io::Read for Buffer {
  fn read(self: &mut Self, buf: &mut [u8]) -> io::Result<usize> {
    let bytes_to_read = ::core::cmp::min(buf.len(), self.data.len() - self.read_offset);
    if bytes_to_read > 0 {
      buf[0..bytes_to_read]
        .clone_from_slice(&self.data[self.read_offset..self.read_offset + bytes_to_read]);
    }
    self.read_offset += bytes_to_read;
    return Ok(bytes_to_read);
  }
}
#[cfg(not(feature="no-stdlib"))]
impl io::Write for Buffer {
  fn write(self: &mut Self, buf: &[u8]) -> io::Result<usize> {
    self.data.extend(buf);
    return Ok(buf.len());
  }
  fn flush(self: &mut Self) -> io::Result<()> {
    return Ok(());
  }
}

<|endoftext|>


<commit_before>use tokenizer::{Token, TokenType};
use std::collections::HashMap;

// type alias for stack
pub type Numeric = f64;

// get operation priority
fn get_priority(operator: &str) -> Option<u8> {
    match operator {
        "(" => Some(0),
        "^" => Some(1),
        "*" | "/" | "%" => Some(2),
        "+" | "-" => Some(3),
        _ => None,
    }
}

// check for the possibility of pulling out of the stack
fn can_pop(op1: &Token, stack: &Vec<Token>) -> bool {
    if stack.len() == 0 {
        return false;
    }
    let p1 = match get_priority(&op1.param) {
        Some(value) => value,
        None => {
            println!("> error: unknown operator '{}'", op1.param);
            return false;
        }
    };
    let last = match stack.last() {
        Some(value) => value,
        None => {
            println!("> error: function stack is empty");
            return false;
        }
    };
    let p2 = match get_priority(&last.param) {
        Some(value) => value,
        None => {
            println!("> error: unknown operator '{}'", last.param);
            return false;
        }
    };
    p1 > 0 && p2 > 0 && p1 >= p2
}

// convert infix notation to reverse polish
fn in2rpn(variables: &mut HashMap<String, Numeric>, tokens: &Vec<Token>) -> Vec<Token> {
    let mut result: Vec<Token> = Vec::new();
    let mut funcs: Vec<Token> = Vec::new();
    let mut token_counter = 0;
    for item in tokens {
        token_counter += 1;
        match item.id {
            // push numeric value to result vector
            TokenType::IsNumeric => {
                result.push(item.clone());
            }
            // get value from hashmap and push to result
            TokenType::IsVariable => {
                if token_counter == 1 {
                    continue;
                }
                let token = match variables.get(&item.param) {
                    Some(value) => format!("{}", value.clone()),
                    None => {
                        println!("> error: can't found {} variable!", item.param);
                        break;
                    }
                };
                result.push(Token::new(token.clone(), TokenType::IsNumeric));
            }
            TokenType::IsFunction => {
                continue;
            }
            TokenType::IsOperation => {
                if item.param == ")" {
                    // push all acumulated functions in brackets
                    while funcs.len() > 0 && funcs.last().unwrap().param != "(" {
                        let function = match funcs.pop() {
                            Some(value) => value,
                            None => {
                                println!("> error: function stack is empty");
                                break;
                            }
                        };
                        result.push(function);
                    }
                    funcs.pop();
                } else if item.param == "=" {
                    continue;
                } else {
                    // push other functions
                    while can_pop(item, &funcs) {
                        let function = match funcs.pop() {
                            Some(value) => value,
                            None => {
                                println!("> error: function stack is empty");
                                break;
                            }
                        };
                        result.push(function);
                    }
                    funcs.push(item.clone());
                }
            }
        }
    }
    // push rest functions
    if funcs.len() > 0 {
        result.push(funcs.pop().unwrap());
    }
    result
}

// reverse polish notation to value
fn rpn2value(line: &Vec<Token>) -> Numeric {
    let mut stack: Vec<Numeric> = Vec::new();
    for token in line {
        if token.id == TokenType::IsOperation {
            // get first operand
            let a = match stack.pop() {
                Some(value) => value,
                None => {
                    println!("> error: stack is empty!");
                    break;
                }
            };
            // get second operand
            let b = match stack.pop() {
                Some(value) => value,
                None => {
                    println!("> error: stack is empty!");
                    break;
                }
            };
            // calculate statement
            let result: Numeric = match token.param.as_ref() {
                "+" => b + a,
                "-" => b - a,
                "*" => b * a,
                "/" => b / a,
                "%" => b % a,
                "^" => b.powf(a),
                _ => continue,
            };
            stack.push(result);
        } else if token.id == TokenType::IsNumeric {
            // add numeric value to stack
            stack.push(match token.param.parse() {
                Ok(value) => value,
                Err(why) => {
                    println!("> error: {}", why);
                    continue;
                }
            });
        }
    }
    // return first value of stack
    *stack.get(0).unwrap()
}

// check tokens type and calculate it
pub fn execute(variables: &mut HashMap<String, Numeric>, tokens: &Vec<Token>) -> bool {
    // get first token
    let first = tokens.get(0).unwrap();
    // get second token
    let operator = if tokens.len() > 1 {
        tokens.get(1)
    } else {
        None
    };
    // if first token in function
    if first.id == TokenType::IsFunction {
        match first.param.as_ref() {
            "print" => {
                let mut calculate_it = false;
                for item in tokens.iter().skip(1) {
                    if item.id == TokenType::IsOperation {
                        calculate_it = true;
                        break;
                    }
                }
                if calculate_it {
                    // convert infix to reverse polish
                    let rpn_statement = in2rpn(variables, tokens);
                    // calucate reverse polish
                    let value = rpn2value(&rpn_statement);
                    println!("{} ", value);
                } else {
                    // iterate by all tokens (except first)
                    for item in tokens.iter().skip(1) {
                        match item.id {
                            // find variable in hashmap and print
                            TokenType::IsVariable => {
                                match variables.get(&item.param) {
                                    Some(var) => println!("{} ", var),
                                    None => {
                                        println!("> error: variable '{}' is not defined!",
                                                 item.param);
                                        continue;
                                    }
                                }
                            }
                            // print numeric value
                            TokenType::IsNumeric => println!("{}", item.param),
                            // say no way!
                            TokenType::IsFunction | TokenType::IsOperation => {
                                println!("type '{}' : {:?}", item.param, item.id)
                            }
                        }
                    }
                }
            }
            "stack" => {
                println!("> variables stack: {:?}", variables);
            }
            "exit" => return true,
            _ => println!("> error: function '{}' not defined", first.param),
        }
    } else {
        let operator = match operator {
            Some(value) => value,
            None => {
                println!("> error: can't execute code");
                return false;
            }
        };
        // if second token is operation -- caclucate statement
        if operator.id == TokenType::IsOperation {
            match operator.param.as_ref() {
                "=" => {
                    // convert infix to reverse polish
                    let rpn_statement = in2rpn(variables, tokens);
                    // calucate reverse polish
                    let value = rpn2value(&rpn_statement);
                    // add value to hashmap
                    variables.insert(first.param.to_owned(), value);
                }
                _ => println!("> error: operator '{}' is not defined!", operator.param),
            }
        }
    }
    false
}
<commit_msg>add 'read' function<commit_after>use tokenizer::{Token, TokenType};
use std::collections::HashMap;
use std::io::prelude::*;
use std::io;

// type alias for stack
pub type Numeric = f64;

// get operation priority
fn get_priority(operator: &str) -> Option<u8> {
    match operator {
        "(" => Some(0),
        "^" => Some(1),
        "*" | "/" | "%" => Some(2),
        "+" | "-" => Some(3),
        _ => None,
    }
}

// check for the possibility of pulling out of the stack
fn can_pop(op1: &Token, stack: &Vec<Token>) -> bool {
    if stack.len() == 0 {
        return false;
    }
    let p1 = match get_priority(&op1.param) {
        Some(value) => value,
        None => {
            println!("> error: unknown operator '{}'", op1.param);
            return false;
        }
    };
    let last = match stack.last() {
        Some(value) => value,
        None => {
            println!("> error: function stack is empty");
            return false;
        }
    };
    let p2 = match get_priority(&last.param) {
        Some(value) => value,
        None => {
            println!("> error: unknown operator '{}'", last.param);
            return false;
        }
    };
    p1 > 0 && p2 > 0 && p1 >= p2
}

// convert infix notation to reverse polish
fn in2rpn(variables: &mut HashMap<String, Numeric>, tokens: &Vec<Token>) -> Vec<Token> {
    let mut result: Vec<Token> = Vec::new();
    let mut funcs: Vec<Token> = Vec::new();
    let mut token_counter = 0;
    for item in tokens {
        token_counter += 1;
        match item.id {
            // push numeric value to result vector
            TokenType::IsNumeric => {
                result.push(item.clone());
            }
            // get value from hashmap and push to result
            TokenType::IsVariable => {
                if token_counter == 1 {
                    continue;
                }
                let token = match variables.get(&item.param) {
                    Some(value) => format!("{}", value.clone()),
                    None => {
                        println!("> error: can't found {} variable!", item.param);
                        break;
                    }
                };
                result.push(Token::new(token.clone(), TokenType::IsNumeric));
            }
            TokenType::IsFunction => {
                continue;
            }
            TokenType::IsOperation => {
                if item.param == ")" {
                    // push all acumulated functions in brackets
                    while funcs.len() > 0 && funcs.last().unwrap().param != "(" {
                        let function = match funcs.pop() {
                            Some(value) => value,
                            None => {
                                println!("> error: function stack is empty");
                                break;
                            }
                        };
                        result.push(function);
                    }
                    funcs.pop();
                } else if item.param == "=" {
                    continue;
                } else {
                    // push other functions
                    while can_pop(item, &funcs) {
                        let function = match funcs.pop() {
                            Some(value) => value,
                            None => {
                                println!("> error: function stack is empty");
                                break;
                            }
                        };
                        result.push(function);
                    }
                    funcs.push(item.clone());
                }
            }
        }
    }
    // push rest functions
    if funcs.len() > 0 {
        result.push(funcs.pop().unwrap());
    }
    result
}

// reverse polish notation to value
fn rpn2value(line: &Vec<Token>) -> Numeric {
    let mut stack: Vec<Numeric> = Vec::new();
    for token in line {
        if token.id == TokenType::IsOperation {
            // get first operand
            let a = match stack.pop() {
                Some(value) => value,
                None => {
                    println!("> error: stack is empty!");
                    break;
                }
            };
            // get second operand
            let b = match stack.pop() {
                Some(value) => value,
                None => {
                    println!("> error: stack is empty!");
                    break;
                }
            };
            // calculate statement
            let result: Numeric = match token.param.as_ref() {
                "+" => b + a,
                "-" => b - a,
                "*" => b * a,
                "/" => b / a,
                "%" => b % a,
                "^" => b.powf(a),
                _ => continue,
            };
            stack.push(result);
        } else if token.id == TokenType::IsNumeric {
            // add numeric value to stack
            stack.push(match token.param.parse() {
                Ok(value) => value,
                Err(why) => {
                    println!("> error: {}", why);
                    continue;
                }
            });
        }
    }
    // return first value of stack
    *stack.get(0).unwrap()
}

// check tokens type and calculate it
pub fn execute(variables: &mut HashMap<String, Numeric>, tokens: &Vec<Token>) -> bool {
    // get first token
    let first = tokens.get(0).unwrap();
    // get second token
    let operator = if tokens.len() > 1 {
        tokens.get(1)
    } else {
        None
    };
    // if first token in function
    if first.id == TokenType::IsFunction {
        match first.param.as_ref() {
            "print" => {
                let mut calculate_it = false;
                for item in tokens.iter().skip(1) {
                    if item.id == TokenType::IsOperation {
                        calculate_it = true;
                        break;
                    }
                }
                if calculate_it {
                    // convert infix to reverse polish
                    let rpn_statement = in2rpn(variables, tokens);
                    // calucate reverse polish
                    let value = rpn2value(&rpn_statement);
                    println!("{} ", value);
                } else {
                    // iterate by all tokens (except first)
                    for item in tokens.iter().skip(1) {
                        match item.id {
                            // find variable in hashmap and print
                            TokenType::IsVariable => {
                                match variables.get(&item.param) {
                                    Some(var) => println!("{} ", var),
                                    None => {
                                        println!("> error: variable '{}' is not defined!",
                                                 item.param);
                                        continue;
                                    }
                                }
                            }
                            // print numeric value
                            TokenType::IsNumeric => println!("{}", item.param),
                            // say no way!
                            TokenType::IsFunction | TokenType::IsOperation => {
                                println!("type '{}' : {:?}", item.param, item.id)
                            }
                        }
                    }
                }
            }
            "read" => {
                let mut buffer = String::new();
                io::stdin().read_line(&mut buffer)
                    .ok()
                    .expect( "[error] Can't read line from stdin!" );
                let value: Numeric = match buffer.trim().parse() {
                    Ok(value) => value,
                    Err(why) => {
                        println!("> error: {}", why);
                        return false;
                    }
                };
                for variable in tokens.iter().skip(1) {
                    variables.insert(variable.param.clone(), value);
                }
            }
            "stack" => {
                println!("> variables stack: {:?}", variables);
            }
            "exit" => return true,
            _ => println!("> error: function '{}' not defined", first.param),
        }
    } else {
        let operator = match operator {
            Some(value) => value,
            None => {
                println!("> error: can't execute code");
                return false;
            }
        };
        // if second token is operation -- caclucate statement
        if operator.id == TokenType::IsOperation {
            match operator.param.as_ref() {
                "=" => {
                    // convert infix to reverse polish
                    let rpn_statement = in2rpn(variables, tokens);
                    // calucate reverse polish
                    let value = rpn2value(&rpn_statement);
                    // add value to hashmap
                    variables.insert(first.param.to_owned(), value);
                }
                _ => println!("> error: operator '{}' is not defined!", operator.param),
            }
        }
    }
    false
}
<|endoftext|>