<commit_before>#![feature(plugin_registrar)]

extern crate rustc;
extern crate syntax;

use std::gc::Gc;
use std::io::Command;
use std::os;
use std::str;

use rustc::plugin::Registry;
use syntax::abi;
use syntax::ast;
use syntax::attr;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, DummyResult, MacroDef};
use syntax::ext::base::MacResult;
use syntax::ext::build::AstBuilder;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::parse::token::{special_idents, InternedString};
use syntax::print::pprust;
use syntax::util::small_vector::SmallVector;

struct LibInfo {
    lib: String,
    statik: bool,
    deps: Vec<(String, bool)>, // (name, static)
    locs: Vec<String>,
}

struct MacItems {
    items: Vec<Gc<ast::Item>>,
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("link_config", expand_link_config);
}

fn expand_link_config(ecx: &mut ExtCtxt, span: Span,
                      tts: &[ast::TokenTree]) -> Box<MacResult> {
    let mut parser = ecx.new_parser_from_tts(tts);
    let (pkg, sp) = match parse_string(ecx, &mut parser) {
        Some(s) => s,
        None => return DummyResult::any(span),
    };
    if !parser.eat(&token::EOF) {
        ecx.span_err(parser.span, "only one string literal allowed");
        return DummyResult::any(span);
    }

    let dyn = match system_pkgconfig(ecx, sp, pkg.as_slice(), false) {
        Some(info) => info,
        None => return DummyResult::any(span),
    };
    let statik = match system_pkgconfig(ecx, sp, pkg.as_slice(), true) {
        Some(info) => info,
        None => return DummyResult::any(span),
    };

    let dyn = block(ecx, sp, &dyn);
    let statik = block(ecx, sp, &statik);

    box MacItems { items: vec![dyn, statik] } as Box<MacResult>
}

fn system_pkgconfig(ecx: &mut ExtCtxt, sp: Span, pkg: &str,
                    statik: bool) -> Option<LibInfo> {

    let mut env = os::env();
    env.push(("PKG_CONFIG_ALLOW_SYSTEM_LIBS".to_string(), "1".to_string()));

    let mut cmd = Command::new("pkg-config");
    cmd.arg(pkg).arg("--libs").env(env.as_slice());
    if statik {
        cmd.arg("--static");
    }
    let out = match cmd.output() {
        Ok(out) => out,
        Err(e) => {
            ecx.span_err(sp, format!("could not run pkg-config: {}", e).as_slice());
            return None
        }
    };
    let stdout = str::from_utf8(out.output.as_slice()).unwrap();
    let stderr = str::from_utf8(out.error.as_slice()).unwrap();
    if !out.status.success() {
        let mut msg = format!("pkg-config did not exit successfully: {}",
                              out.status);
        if stdout.len() > 0 {
            msg.push_str("\n--- stdout\n");
            msg.push_str(stdout);
        }
        if stderr.len() > 0 {
            msg.push_str("\n--- stderr\n");
            msg.push_str(stderr);
        }
        ecx.span_err(sp, msg.as_slice());
        return None
    }

    let mut libs = Vec::new();
    let mut locs = Vec::new();
    for arg in stdout.split(' ').filter(|l| !l.is_empty()) {
        if arg.starts_with("-l") {
            libs.push(arg.slice_from(2));
        } else if arg.starts_with("-L") {
            locs.push(arg.slice_from(2).to_string());
        }
    }

    let libs = libs.move_iter().map(|lib| {
        (lib.to_string(), statik && locs.iter().any(|l| {
            let base = Path::new(l.as_slice());
            base.join(format!("lib{}.a", lib)).exists() ||
                base.join(format!("{}.lib", lib)).exists() ||
                base.join(format!("lib{}.lib", lib)).exists()
        }))
    }).collect();
    Some(LibInfo {
        lib: pkg.to_string(),
        deps: libs,
        locs: locs,
        statik: statik,
    })
}

fn block(ecx: &mut ExtCtxt, sp: Span, info: &LibInfo) -> Gc<ast::Item> {
    let lib = token::intern_and_get_ident(info.lib.as_slice());
    let cfg = ecx.meta_name_value(sp, InternedString::new("statik"),
                                  ast::LitStr(lib, ast::CookedStr));
    let cfg = ecx.meta_list(sp, InternedString::new("cfg"), vec![if info.statik {
        cfg
    } else {
        ecx.meta_list(sp, InternedString::new("not"), vec![cfg])
    }]);

    let attrs = vec![ecx.attribute(sp, cfg)].move_iter();
    let attrs = attrs.chain(info.deps.iter().map(|&(ref l, statik)| {
        // Build #[link(name = <l>)]
        let l = token::intern_and_get_ident(l.as_slice());
        let mut items = Vec::new();
        items.push(ecx.meta_name_value(sp, InternedString::new("name"),
                                       ast::LitStr(l, ast::CookedStr)));
        if statik {
            let l = InternedString::new("static");
            items.push(ecx.meta_name_value(sp, InternedString::new("kind"),
                                           ast::LitStr(l, ast::CookedStr)));
        }
        let list = ecx.meta_list(sp, InternedString::new("link"), items);
        ecx.attribute(sp, list)
    }));
    let attrs = attrs.chain(info.locs.iter().map(|l| {
        let l = token::intern_and_get_ident(format!("-L{}", l).as_slice());
        let args = ecx.meta_name_value(sp, InternedString::new("link_args"),
                                       ast::LitStr(l, ast::CookedStr));
        ecx.attribute(sp, args)
    }));
    let mut attrs = attrs.map(|attr| {
        attr::mark_used(&attr);
        attr
    });

    ecx.item(sp, special_idents::invalid, attrs.collect(),
             ast::ItemForeignMod(ast::ForeignMod {
        abi: abi::C,
        view_items: Vec::new(),
        items: Vec::new(),
    }))
}

fn parse_string(ecx: &mut ExtCtxt,
                parser: &mut Parser) -> Option<(String, Span)> {
    let entry = ecx.expand_expr(parser.parse_expr());
    match entry.node {
        ast::ExprLit(lit) => {
            match lit.node {
                ast::LitStr(ref s, _) => return Some((s.to_str(), entry.span)),
                _ => {}
            }
        }
        _ => {}
    }
    ecx.span_err(entry.span, format!(
        "expected string literal but got `{}`",
        pprust::expr_to_str(entry)).as_slice());
    None
}

impl MacResult for MacItems {
    fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
        Some(self.items.iter().map(|a| a.clone()).collect())
    }
    fn make_stmt(&self) -> Option<Gc<ast::Stmt>> { None }
    fn make_def(&self) -> Option<MacroDef> { None }
    fn make_expr(&self) -> Option<Gc<ast::Expr>> { None }
    fn make_pat(&self) -> Option<Gc<ast::Pat>> { None }
}
<commit_msg>Update to master<commit_after>#![feature(plugin_registrar)]

extern crate rustc;
extern crate syntax;

use std::gc::Gc;
use std::io::Command;
use std::str;

use rustc::plugin::Registry;
use syntax::abi;
use syntax::ast;
use syntax::attr;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, DummyResult, MacroDef};
use syntax::ext::base::MacResult;
use syntax::ext::build::AstBuilder;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::parse::token::{special_idents, InternedString};
use syntax::print::pprust;
use syntax::util::small_vector::SmallVector;

struct LibInfo {
    lib: String,
    statik: bool,
    deps: Vec<(String, bool)>, // (name, static)
    locs: Vec<String>,
}

struct MacItems {
    items: Vec<Gc<ast::Item>>,
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("link_config", expand_link_config);
}

fn expand_link_config(ecx: &mut ExtCtxt, span: Span,
                      tts: &[ast::TokenTree]) -> Box<MacResult> {
    let mut parser = ecx.new_parser_from_tts(tts);
    let (pkg, sp) = match parse_string(ecx, &mut parser) {
        Some(s) => s,
        None => return DummyResult::any(span),
    };
    if !parser.eat(&token::EOF) {
        ecx.span_err(parser.span, "only one string literal allowed");
        return DummyResult::any(span);
    }

    let dyn = match system_pkgconfig(ecx, sp, pkg.as_slice(), false) {
        Some(info) => info,
        None => return DummyResult::any(span),
    };
    let statik = match system_pkgconfig(ecx, sp, pkg.as_slice(), true) {
        Some(info) => info,
        None => return DummyResult::any(span),
    };

    let dyn = block(ecx, sp, &dyn);
    let statik = block(ecx, sp, &statik);

    box MacItems { items: vec![dyn, statik] } as Box<MacResult>
}

fn system_pkgconfig(ecx: &mut ExtCtxt, sp: Span, pkg: &str,
                    statik: bool) -> Option<LibInfo> {

    let mut cmd = Command::new("pkg-config");
    cmd.arg(pkg).arg("--libs").env("PKG_CONFIG_ALLOW_SYSTEM_LIBS", "1");
    if statik {
        cmd.arg("--static");
    }
    let out = match cmd.output() {
        Ok(out) => out,
        Err(e) => {
            ecx.span_err(sp, format!("could not run pkg-config: {}", e).as_slice());
            return None
        }
    };
    let stdout = str::from_utf8(out.output.as_slice()).unwrap();
    let stderr = str::from_utf8(out.error.as_slice()).unwrap();
    if !out.status.success() {
        let mut msg = format!("pkg-config did not exit successfully: {}",
                              out.status);
        if stdout.len() > 0 {
            msg.push_str("\n--- stdout\n");
            msg.push_str(stdout);
        }
        if stderr.len() > 0 {
            msg.push_str("\n--- stderr\n");
            msg.push_str(stderr);
        }
        ecx.span_err(sp, msg.as_slice());
        return None
    }

    let mut libs = Vec::new();
    let mut locs = Vec::new();
    for arg in stdout.split(' ').filter(|l| !l.is_empty()) {
        if arg.starts_with("-l") {
            libs.push(arg.slice_from(2));
        } else if arg.starts_with("-L") {
            locs.push(arg.slice_from(2).to_string());
        }
    }

    let libs = libs.move_iter().map(|lib| {
        (lib.to_string(), statik && locs.iter().any(|l| {
            let base = Path::new(l.as_slice());
            base.join(format!("lib{}.a", lib)).exists() ||
                base.join(format!("{}.lib", lib)).exists() ||
                base.join(format!("lib{}.lib", lib)).exists()
        }))
    }).collect();
    Some(LibInfo {
        lib: pkg.to_string(),
        deps: libs,
        locs: locs,
        statik: statik,
    })
}

fn block(ecx: &mut ExtCtxt, sp: Span, info: &LibInfo) -> Gc<ast::Item> {
    let lib = token::intern_and_get_ident(info.lib.as_slice());
    let cfg = ecx.meta_name_value(sp, InternedString::new("statik"),
                                  ast::LitStr(lib, ast::CookedStr));
    let cfg = ecx.meta_list(sp, InternedString::new("cfg"), vec![if info.statik {
        cfg
    } else {
        ecx.meta_list(sp, InternedString::new("not"), vec![cfg])
    }]);

    let attrs = vec![ecx.attribute(sp, cfg)].move_iter();
    let attrs = attrs.chain(info.deps.iter().map(|&(ref l, statik)| {
        // Build #[link(name = <l>)]
        let l = token::intern_and_get_ident(l.as_slice());
        let mut items = Vec::new();
        items.push(ecx.meta_name_value(sp, InternedString::new("name"),
                                       ast::LitStr(l, ast::CookedStr)));
        if statik {
            let l = InternedString::new("static");
            items.push(ecx.meta_name_value(sp, InternedString::new("kind"),
                                           ast::LitStr(l, ast::CookedStr)));
        }
        let list = ecx.meta_list(sp, InternedString::new("link"), items);
        ecx.attribute(sp, list)
    }));
    let attrs = attrs.chain(info.locs.iter().map(|l| {
        let l = token::intern_and_get_ident(format!("-L{}", l).as_slice());
        let args = ecx.meta_name_value(sp, InternedString::new("link_args"),
                                       ast::LitStr(l, ast::CookedStr));
        ecx.attribute(sp, args)
    }));
    let mut attrs = attrs.map(|attr| {
        attr::mark_used(&attr);
        attr
    });

    ecx.item(sp, special_idents::invalid, attrs.collect(),
             ast::ItemForeignMod(ast::ForeignMod {
        abi: abi::C,
        view_items: Vec::new(),
        items: Vec::new(),
    }))
}

fn parse_string(ecx: &mut ExtCtxt,
                parser: &mut Parser) -> Option<(String, Span)> {
    let entry = ecx.expand_expr(parser.parse_expr());
    match entry.node {
        ast::ExprLit(lit) => {
            match lit.node {
                ast::LitStr(ref s, _) => return Some((s.to_string(), entry.span)),
                _ => {}
            }
        }
        _ => {}
    }
    ecx.span_err(entry.span, format!(
        "expected string literal but got `{}`",
        pprust::expr_to_string(entry)).as_slice());
    None
}

impl MacResult for MacItems {
    fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
        Some(self.items.iter().map(|a| a.clone()).collect())
    }
    fn make_stmt(&self) -> Option<Gc<ast::Stmt>> { None }
    fn make_def(&self) -> Option<MacroDef> { None }
    fn make_expr(&self) -> Option<Gc<ast::Expr>> { None }
    fn make_pat(&self) -> Option<Gc<ast::Pat>> { None }
}
<|endoftext|>


<commit_before>//! indicatif is a library for Rust that helps you build command line
//! interfaces that report progress to users.  It comes with various
//! tools and utilities for formatting anything that indicates progress.
//!
//! Platform support:
//!
//! * Linux
//! * OS X
//! * Windows (colors require Windows 10)
//!
//! Best paired with other libraries in the family:
//!
//! * [console](https://docs.rs/console)
//! * [dialoguer](https://docs.rs/dialoguer)
//!
//! # Crate Contents
//!
//! * **Progress bars**
//!   * [`ProgressBar`](struct.ProgressBar.html) for bars and spinners
//!   * [`MultiProgress`](struct.MultiProgress.html) for multiple bars
//! * **Data Formatting**
//!   * [`HumanBytes`](struct.HumanBytes.html) for formatting bytes
//!   * [`DecimalBytes`](struct.DecimalBytes.html) for formatting bytes using SI prefixes
//!   * [`BinaryBytes`](struct.BinaryBytes.html) for formatting bytes using ISO/IEC prefixes
//!   * [`HumanDuration`](struct.HumanDuration.html) for formatting durations
//!
//! # Progress Bars and Spinners
//!
//! indicatif comes with a `ProgressBar` type that supports both bounded
//! progress bar uses as well as unbounded "spinner" type progress reports.
//! Progress bars are `Sync` and `Send` objects which means that they are
//! internally locked and can be passed from thread to thread.
//!
//! Additionally a `MultiProgress` utility is provided that can manage
//! rendering multiple progress bars at once (eg: from multiple threads).
//!
//! To whet your appetite, this is what this can look like:
//!
//!
//!
//! Progress bars are manually advanced and by default draw to stderr.
//! When you are done, the progress bar can be finished either visibly
//! (eg: the progress bar stays on the screen) or cleared (the progress
//! bar will be removed).
//!
//! ```rust
//! use indicatif::ProgressBar;
//!
//! let bar = ProgressBar::new(1000);
//! for _ in 0..1000 {
//!     bar.inc(1);
//!     // ...
//! }
//! bar.finish();
//! ```
//!
//! General progress bar behaviors:
//!
//! * if a non terminal is detected the progress bar will be completely
//!   hidden.  This makes piping programs to logfiles make sense out of
//!   the box.
//! * a progress bar only starts drawing when `set_message`, `inc`, `set_position`
//!   or `tick` are called.  In some situations you might have to call `tick`
//!   once to draw it.
//! * progress bars should be explicitly finished to reset the rendering
//!   for others.  Either by also clearing them or by replacing them with
//!   a new message / retaining the current message.
//! * the default template renders neither message nor prefix.
//!
//! # Templates
//!
//! Progress bars can be styled with simple format strings similar to the
//! ones in Rust itself.  The format for a placeholder is `{key:options}`
//! where the `options` part is optional.  If provided the format is this:
//!
//! ```text
//! [<^>]           for an optional alignment specification
//! WIDTH           an optional width as positive integer
//! !               an optional exclamation mark to enable truncation
//! .STYLE          an optional dot separated style string
//! /STYLE          an optional dot separated alternative style string
//! ```
//!
//! For the style component see `Styled::from_dotted_str` for more
//! information.
//!
//! Some examples for templates:
//!
//! ```text
//! [{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}
//! ```
//!
//! This sets a progress bar that is 40 characters wide and has cyan
//! as primary style color and blue as alternative style color.
//! Alternative styles are currently only used for progress bars.
//!
//! Example configuration:
//!
//! ```ignore
//! bar.set_style(ProgressStyle::default_bar()
//!     .template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}")
//!     .progress_chars("##-"));
//! ```
//!
//! The following keys exist:
//!
//! * `bar`: renders a progress bar. By default 20 characters wide.  The
//!   style string is used to color the elapsed part, the alternative
//!   style is used for the bar that is yet to render.
//! * `wide_bar`: like `bar` but always fills the remaining space.
//! * `spinner`: renders the spinner (current tick char)
//! * `prefix`: renders the prefix set on the progress bar.
//! * `msg`: renders the currently set message on the progress bar.
//! * `wide_msg`: like `msg` but always fills the remaining space and truncates.
//! * `pos`: renders the current position of the bar as integer
//! * `len`: renders the total length of the bar as integer
//! * `bytes`: renders the current position of the bar as bytes.
//! * `percent`: renders the current position of the bar as a percentage of the total length.
//! * `total_bytes`: renders the total length of the bar as bytes.
//! * `elapsed_precise`: renders the elapsed time as `HH:MM:SS`.
//! * `elapsed`: renders the elapsed time as `42s`, `1m` etc.
//! * `eta_precise`: the remaining time (like `elapsed_precise`).
//! * `eta`: the remaining time (like `elapsed`).
//!
//! The design of the progress bar can be altered with the integrated
//! template functionality.  The template can be set by changing a
//! `ProgressStyle` and attaching it to the progress bar.
//!
//! # Human Readable Formatting
//!
//! There are some formatting wrappers for showing elapsed time and
//! file sizes for human users:
//!
//! ```ignore
//! use std::time::Instant;
//! use indicatif::{HumanDuration, HumanBytes};
//!
//! let started = Instant::now();
//! println!("The file is {} large", HumanBytes(file.size));
//! println!("The script took {}", HumanDuration(started.elapsed()));
//! ```
extern crate parking_lot;
extern crate regex;
#[macro_use]
extern crate lazy_static;
extern crate console;
extern crate number_prefix;

mod format;
mod progress;
mod style;
mod utils;

pub use format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
pub use progress::{
    MultiProgress, ProgressBar, ProgressBarIter, ProgressBarRead, ProgressDrawTarget,
};
pub use style::ProgressStyle;
<commit_msg>Link to console docs for colors.  Fixes #84<commit_after>//! indicatif is a library for Rust that helps you build command line
//! interfaces that report progress to users.  It comes with various
//! tools and utilities for formatting anything that indicates progress.
//!
//! Platform support:
//!
//! * Linux
//! * OS X
//! * Windows (colors require Windows 10)
//!
//! Best paired with other libraries in the family:
//!
//! * [console](https://docs.rs/console)
//! * [dialoguer](https://docs.rs/dialoguer)
//!
//! # Crate Contents
//!
//! * **Progress bars**
//!   * [`ProgressBar`](struct.ProgressBar.html) for bars and spinners
//!   * [`MultiProgress`](struct.MultiProgress.html) for multiple bars
//! * **Data Formatting**
//!   * [`HumanBytes`](struct.HumanBytes.html) for formatting bytes
//!   * [`DecimalBytes`](struct.DecimalBytes.html) for formatting bytes using SI prefixes
//!   * [`BinaryBytes`](struct.BinaryBytes.html) for formatting bytes using ISO/IEC prefixes
//!   * [`HumanDuration`](struct.HumanDuration.html) for formatting durations
//!
//! # Progress Bars and Spinners
//!
//! indicatif comes with a `ProgressBar` type that supports both bounded
//! progress bar uses as well as unbounded "spinner" type progress reports.
//! Progress bars are `Sync` and `Send` objects which means that they are
//! internally locked and can be passed from thread to thread.
//!
//! Additionally a `MultiProgress` utility is provided that can manage
//! rendering multiple progress bars at once (eg: from multiple threads).
//!
//! To whet your appetite, this is what this can look like:
//!
//!
//!
//! Progress bars are manually advanced and by default draw to stderr.
//! When you are done, the progress bar can be finished either visibly
//! (eg: the progress bar stays on the screen) or cleared (the progress
//! bar will be removed).
//!
//! ```rust
//! use indicatif::ProgressBar;
//!
//! let bar = ProgressBar::new(1000);
//! for _ in 0..1000 {
//!     bar.inc(1);
//!     // ...
//! }
//! bar.finish();
//! ```
//!
//! General progress bar behaviors:
//!
//! * if a non terminal is detected the progress bar will be completely
//!   hidden.  This makes piping programs to logfiles make sense out of
//!   the box.
//! * a progress bar only starts drawing when `set_message`, `inc`, `set_position`
//!   or `tick` are called.  In some situations you might have to call `tick`
//!   once to draw it.
//! * progress bars should be explicitly finished to reset the rendering
//!   for others.  Either by also clearing them or by replacing them with
//!   a new message / retaining the current message.
//! * the default template renders neither message nor prefix.
//!
//! # Templates
//!
//! Progress bars can be styled with simple format strings similar to the
//! ones in Rust itself.  The format for a placeholder is `{key:options}`
//! where the `options` part is optional.  If provided the format is this:
//!
//! ```text
//! [<^>]           for an optional alignment specification
//! WIDTH           an optional width as positive integer
//! !               an optional exclamation mark to enable truncation
//! .STYLE          an optional dot separated style string
//! /STYLE          an optional dot separated alternative style string
//! ```
//!
//! For the style component see [`Style::from_dotted_str`](https://docs.rs/console/0.7.5/console/struct.Style.html#method.from_dotted_str)
//! for more information.  Indicatif uses the `console` base crate for all
//! colorization and formatting options.
//!
//! Some examples for templates:
//!
//! ```text
//! [{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}
//! ```
//!
//! This sets a progress bar that is 40 characters wide and has cyan
//! as primary style color and blue as alternative style color.
//! Alternative styles are currently only used for progress bars.
//!
//! Example configuration:
//!
//! ```ignore
//! bar.set_style(ProgressStyle::default_bar()
//!     .template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}")
//!     .progress_chars("##-"));
//! ```
//!
//! The following keys exist:
//!
//! * `bar`: renders a progress bar. By default 20 characters wide.  The
//!   style string is used to color the elapsed part, the alternative
//!   style is used for the bar that is yet to render.
//! * `wide_bar`: like `bar` but always fills the remaining space.
//! * `spinner`: renders the spinner (current tick char)
//! * `prefix`: renders the prefix set on the progress bar.
//! * `msg`: renders the currently set message on the progress bar.
//! * `wide_msg`: like `msg` but always fills the remaining space and truncates.
//! * `pos`: renders the current position of the bar as integer
//! * `len`: renders the total length of the bar as integer
//! * `bytes`: renders the current position of the bar as bytes.
//! * `percent`: renders the current position of the bar as a percentage of the total length.
//! * `total_bytes`: renders the total length of the bar as bytes.
//! * `elapsed_precise`: renders the elapsed time as `HH:MM:SS`.
//! * `elapsed`: renders the elapsed time as `42s`, `1m` etc.
//! * `eta_precise`: the remaining time (like `elapsed_precise`).
//! * `eta`: the remaining time (like `elapsed`).
//!
//! The design of the progress bar can be altered with the integrated
//! template functionality.  The template can be set by changing a
//! `ProgressStyle` and attaching it to the progress bar.
//!
//! # Human Readable Formatting
//!
//! There are some formatting wrappers for showing elapsed time and
//! file sizes for human users:
//!
//! ```ignore
//! use std::time::Instant;
//! use indicatif::{HumanDuration, HumanBytes};
//!
//! let started = Instant::now();
//! println!("The file is {} large", HumanBytes(file.size));
//! println!("The script took {}", HumanDuration(started.elapsed()));
//! ```
extern crate parking_lot;
extern crate regex;
#[macro_use]
extern crate lazy_static;
extern crate console;
extern crate number_prefix;

mod format;
mod progress;
mod style;
mod utils;

pub use format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
pub use progress::{
    MultiProgress, ProgressBar, ProgressBarIter, ProgressBarRead, ProgressDrawTarget,
};
pub use style::ProgressStyle;
<|endoftext|>


<commit_before>extern crate rmp as msgpack;
extern crate redis;
extern crate rustc_serialize;

use std::collections::HashMap;

use redis::Commands;

mod encoder;
use encoder::*;

mod decoder;
use decoder::*;

mod save;
use save::SAVE;

pub trait Ohmer : rustc_serialize::Encodable + rustc_serialize::Decodable + Sized {
    fn id_field(&self) -> String { "id".to_string() }
    fn id(&self) -> usize;
    fn set_id(&mut self, id: usize);

    fn defaults() -> Self;
    fn get<T: Ohmer>(id: usize, r: &redis::Client) -> Result<T, DecoderError> {
        let mut obj = T::defaults();
        try!(obj.load(id, r));
        Ok(obj)
    }

    fn get_class_name(&self) -> String {
        let mut encoder = Encoder::new();
        self.encode(&mut encoder).unwrap();
        encoder.features.remove("name").unwrap()
    }

    fn load(&mut self, id: usize, r: &redis::Client) -> Result<(), DecoderError> {
        let mut properties:HashMap<String, String> = try!(try!(r.get_connection()).hgetall(format!("{}:{}", "Person", id)));
        properties.insert("id".to_string(), format!("{}", id));

        let mut decoder = Decoder::new(properties);
        *self = try!(rustc_serialize::Decodable::decode(&mut decoder));
        Ok(())
    }

    fn save(&mut self, r: &redis::Client) -> Result<(), EncoderError>{
        let mut encoder = Encoder::new();
        encoder.id_field = self.id_field();
        try!(self.encode(&mut encoder));
        let script = redis::Script::new(SAVE);
        let id = try!(script
                .arg(try!(msgpack_encode(&encoder.features)))
                .arg(try!(msgpack_encode(&encoder.attributes.iter().map(|x| &*x).collect::<Vec<_>>())))
                .arg(try!(msgpack_encode(&Vec::new() as &Vec<u8>)))
                .arg(try!(msgpack_encode(&Vec::new() as &Vec<u8>)))
                .invoke(&try!(r.get_connection())));
        self.set_id(id);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use redis;
    use rustc_serialize::Encodable;

    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct Person {
        id: usize,
        name: String,
    }
    impl Ohmer for Person {
        fn id(&self) -> usize { self.id }
        fn set_id(&mut self, id: usize) { self.id = id; }
        fn defaults() -> Self {
            Person {
                id: 0,
                name: "".to_string(),
            }
        }
    }

    #[test]
    fn test_save() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let mut person = Person { id: 0, name: "John".to_string() };
        person.save(&client).unwrap();
    }

    #[test]
    fn test_save_load() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let mut person = Person { id: 0, name: "Jane".to_string() };
        person.save(&client).unwrap();
        let person2 = Person::get::<Person>(person.id, &client).unwrap();
        assert_eq!(person, person2);
    }
}
<commit_msg>Extract get<commit_after>extern crate rmp as msgpack;
extern crate redis;
extern crate rustc_serialize;

use std::collections::HashMap;

use redis::Commands;

mod encoder;
use encoder::*;

mod decoder;
use decoder::*;

mod save;
use save::SAVE;

pub fn get<T: Ohmer>(id: usize, r: &redis::Client) -> Result<T, DecoderError> {
    let mut obj = T::defaults();
    try!(obj.load(id, r));
    Ok(obj)
}

pub trait Ohmer : rustc_serialize::Encodable + rustc_serialize::Decodable + Sized {
    fn id_field(&self) -> String { "id".to_string() }
    fn id(&self) -> usize;
    fn set_id(&mut self, id: usize);

    fn defaults() -> Self;

    fn get_class_name(&self) -> String {
        let mut encoder = Encoder::new();
        self.encode(&mut encoder).unwrap();
        encoder.features.remove("name").unwrap()
    }

    fn load(&mut self, id: usize, r: &redis::Client) -> Result<(), DecoderError> {
        let mut properties:HashMap<String, String> = try!(try!(r.get_connection()).hgetall(format!("{}:{}", "Person", id)));
        properties.insert("id".to_string(), format!("{}", id));

        let mut decoder = Decoder::new(properties);
        *self = try!(rustc_serialize::Decodable::decode(&mut decoder));
        Ok(())
    }

    fn save(&mut self, r: &redis::Client) -> Result<(), EncoderError>{
        let mut encoder = Encoder::new();
        encoder.id_field = self.id_field();
        try!(self.encode(&mut encoder));
        let script = redis::Script::new(SAVE);
        let id = try!(script
                .arg(try!(msgpack_encode(&encoder.features)))
                .arg(try!(msgpack_encode(&encoder.attributes.iter().map(|x| &*x).collect::<Vec<_>>())))
                .arg(try!(msgpack_encode(&Vec::new() as &Vec<u8>)))
                .arg(try!(msgpack_encode(&Vec::new() as &Vec<u8>)))
                .invoke(&try!(r.get_connection())));
        self.set_id(id);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use redis;
    use rustc_serialize::Encodable;

    #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
    struct Person {
        id: usize,
        name: String,
    }
    impl Ohmer for Person {
        fn id(&self) -> usize { self.id }
        fn set_id(&mut self, id: usize) { self.id = id; }
        fn defaults() -> Self {
            Person {
                id: 0,
                name: "".to_string(),
            }
        }
    }

    #[test]
    fn test_save() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let mut person = Person { id: 0, name: "John".to_string() };
        person.save(&client).unwrap();
    }

    #[test]
    fn test_save_load() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let mut person = Person { id: 0, name: "Jane".to_string() };
        person.save(&client).unwrap();
        let person2 = get(person.id, &client).unwrap();
        assert_eq!(person, person2);
    }
}
<|endoftext|>


<commit_before>//! Crate to detect image borders and whitespace using entropy-based image border detection
//! algorithm.
//!
//! # Example
//!
//! ```rust,ignore
//! extern crate enimda;
//!
//! use std::path::Path;
//! use enimda::enimda;
//!
//! let path = Path::new("test.jpg");
//! let borders = enimda(&path, 1.0, 100, 2048, 0.25, 0.5, 1.0, 2048, true)?;
//!
//! println!("{:?}", borders);
//! ```

#![deny(missing_docs)]

extern crate rand;
extern crate image;
extern crate gif;
extern crate gif_dispose;
extern crate image_utils;

use std::path::Path;
use std::error::Error;
use image::ImageFormat;
use image_utils::info;

mod utils;

use utils::{decompose, scan};

/// enimda function result
#[derive(Debug, PartialEq)]
pub struct Borders {
    /// Border offset from top
    pub top: u32,
    /// Border offset from right
    pub right: u32,
    /// Border offset from bottom
    pub bottom: u32,
    /// Border offset from left
    pub left: u32,
}

/// Scan image and find its borders
/// * `path` - path to image file
/// * `fppt` - percent of frames to use in case of animated image, optimization parameter
/// * `flim` - absolute limit of frames to use in case of animated image, optimization parameter
/// * `size` - fit image to this size to improve performance, in pixels, optimization parameter
/// * `depth` - percent of pixels (depth) to use for scanning, use 0.25 if not sure what are you
/// doing
/// * `thres` - threshold, aggressiveness of algorithm, use 0.5 if not sure what are you doing
/// * `sppt` - percent of columns to use for scan, optimization parameter
/// * `slim` - absolute limit of columns to use for scan, optimization parameter
/// * `deep` - set to true for less performant but accurate and to false for quick but inaccurate,
/// optimization parameter
pub fn enimda(path: &Path,
              fppt: f32,
              flim: u32,
              size: u32,
              depth: f32,
              thres: f32,
              sppt: f32,
              slim: u32,
              deep: bool)
              -> Result<Borders, Box<Error>> {

    let inf = info(path)?;

    let borders = match inf.format {
        ImageFormat::GIF => {
            let ims = decompose(path, inf.width, inf.height, inf.frames, fppt, flim)?;

            let mut borders = vec![0, 0, 0, 0];
            for im in ims.iter() {
                let variant = scan(&im, size, depth, thres, sppt, slim, deep)?;
                for i in 0..borders.len() {
                    if variant[i] == 0 || borders[i] < variant[i] {
                        borders[i] = variant[i];
                    }
                }
            }

            borders
        }
        _ => {
            let im = image::open(path)?;
            scan(&im, size, depth, thres, sppt, slim, deep)?
        }
    };

    Ok(Borders {
        top: borders[0],
        right: borders[1],
        bottom: borders[2],
        left: borders[3],
    })
}
<commit_msg>docstring<commit_after>//! Crate to detect image borders and whitespace using entropy-based image border detection
//! algorithm.
//!
//! # Example
//!
//! ```rust,ignore
//! extern crate enimda;
//!
//! use std::path::Path;
//! use enimda::enimda;
//!
//! let path = Path::new("test.jpg");
//! let borders = enimda(&path, 1.0, 100, 2048, 0.25, 0.5, 1.0, 2048, true)?;
//!
//! println!("{:?}", borders);
//! ```

#![deny(missing_docs)]

extern crate rand;
extern crate image;
extern crate gif;
extern crate gif_dispose;
extern crate image_utils;

use std::path::Path;
use std::error::Error;
use image::ImageFormat;
use image_utils::info;

mod utils;

use utils::{decompose, scan};

/// enimda function result
#[derive(Debug, PartialEq)]
pub struct Borders {
    /// Border offset from the top
    pub top: u32,
    /// Border offset from the right
    pub right: u32,
    /// Border offset from the bottom
    pub bottom: u32,
    /// Border offset from the left
    pub left: u32,
}

/// Scan image and find its borders
///
/// `path` - path to image file
///
/// `fppt` - percent of frames to use in case of animated image, optimization parameter
///
/// `flim` - absolute limit of frames to use in case of animated image, optimization parameter
///
/// `size` - fit image to this size to improve performance, in pixels, optimization parameter
///
/// `depth` - percent of pixels (depth) to use for scanning, use 0.25 if not sure what are you
/// doing
///
/// `thres` - threshold, aggressiveness of algorithm, use 0.5 if not sure what are you doing
///
/// `sppt` - percent of columns to use for scan, optimization parameter
///
/// `slim` - absolute limit of columns to use for scan, optimization parameter
///
/// `deep` - set to true for less performant but accurate and to false for quick but inaccurate,
/// optimization parameter
pub fn enimda(path: &Path,
              fppt: f32,
              flim: u32,
              size: u32,
              depth: f32,
              thres: f32,
              sppt: f32,
              slim: u32,
              deep: bool)
              -> Result<Borders, Box<Error>> {

    let inf = info(path)?;

    let borders = match inf.format {
        ImageFormat::GIF => {
            let ims = decompose(path, inf.width, inf.height, inf.frames, fppt, flim)?;

            let mut borders = vec![0, 0, 0, 0];
            for im in ims.iter() {
                let variant = scan(&im, size, depth, thres, sppt, slim, deep)?;
                for i in 0..borders.len() {
                    if variant[i] == 0 || borders[i] < variant[i] {
                        borders[i] = variant[i];
                    }
                }
            }

            borders
        }
        _ => {
            let im = image::open(path)?;
            scan(&im, size, depth, thres, sppt, slim, deep)?
        }
    };

    Ok(Borders {
        top: borders[0],
        right: borders[1],
        bottom: borders[2],
        left: borders[3],
    })
}
<|endoftext|>


<commit_before>extern crate bincode;
extern crate rustc_serialize;
extern crate rand;

mod wal_file;
mod multi_map;

use wal_file::{KeyValuePair, WALFile, WALIterator};
// use multi_map::{MultiMap, MultiMapIterator};

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, decode};
use rustc_serialize::{Encodable, Decodable};

use std::cmp::max;
use std::convert::From;
use std::collections::{BTreeMap, BTreeSet};
use std::error::Error;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::{Read, Write, Seek, SeekFrom, ErrorKind};
use std::mem::{size_of};
use std::str;

const NUM_CHILDREN: usize = 32;
const FILE_HEADER: &'static str = "B+Tree\0";
const CURRENT_VERSION: u8 = 0x01;

// specify the types for the keys & values
pub trait KeyType: Ord + Encodable + Decodable + Clone {}
pub trait ValueType: Ord + Encodable + Decodable + Clone  {}

// provide generic implementations

impl<T> KeyType for T where T: Ord + Encodable + Decodable + Clone {}
impl<T> ValueType for T where T: Ord + Encodable + Decodable + Clone {}

#[derive(RustcEncodable, RustcDecodable, PartialEq)]
enum Payload<K: KeyType, V: ValueType> {
        Value(V),
        Children([(K,u64); NUM_CHILDREN]),
    }

#[derive(RustcEncodable, RustcDecodable, PartialEq)]
struct Node<K: KeyType, V: ValueType> {
    key: K,
    parent: u64,
    payload: Payload<K,V>, // either children, or actual values
}

/// This struct represents an on-disk B+Tree. There are NUM_CHILDREN keys at each
/// level in the tree. The on-disk format is as follows where VV is the version
/// number:
/// |-------------------------------------------|
/// | 0x42 0x2b 0x54 0x72 | 0x65 0x65 0x00 0xVV |
/// | B    +    T    r    | e    e    \0   0xVV |
/// |-------------------------------------------|
/// | smallest record in bincode format         |
/// |-------------------------------------------|
/// | ...                                       |
/// |-------------------------------------------|
/// | largest record in bincode format          |
/// |-------------------------------------------|
/// | internal nodes ...                        |
/// |-------------------------------------------|
/// | root node                                 |
/// |-------------------------------------------|
pub struct BTree<K: KeyType, V: ValueType> {
    tree_file_path: String,         // the path to the tree file
    tree_file: File,                // the file backing the whole thing
    wal_file: WALFile<K,V>,              // write-ahead log for in-memory items
    root: Option<Node<K,V>>,        // optional in-memory copy of the root node
    max_key_size: usize,            // the size of the key in bytes
    max_value_size: usize,          // the size of the value in bytes
    mem_tree: BTreeMap<K, BTreeSet<V>>,  // the in-memory BTree that gets merged with the on-disk one
}

impl <K: KeyType, V: ValueType> BTree<K, V> {
    pub fn new(tree_file_path: String, max_key_size: usize, max_value_size: usize) -> Result<BTree<K,V>, Box<Error>> {
        // create our mem_tree
        let mut mem_tree = BTreeMap::<K, BTreeSet<V>>::new();

        let wal_file_path = tree_file_path.to_owned() + ".wal";

        let mut wal_file = try!(WALFile::<K,V>::new(wal_file_path.to_owned(), max_key_size, max_value_size));

        let record_size = max_key_size + max_value_size;

        // if we have a WAL file, replay it into the mem_tree
        if try!(wal_file.is_new()) {
            for kv in &mut wal_file {
                mem_tree.entry(kv.key).or_insert(BTreeSet::<V>::new()).insert(kv.value);
            }
        }

        // compute the size of a on-disk Node
        let node_size: usize = (max_key_size + size_of::<u64>() + max(max_value_size, (max_key_size + size_of::<u64>()) * NUM_CHILDREN)) as usize;

        // open the data file
        let mut tree_file = try!(OpenOptions::new().read(true).write(true).create(true).open(tree_file_path.to_owned()));

        let metadata = try!(tree_file.metadata());

        // check to see if this is a new file
        if metadata.len() == 0 {
            // write out our header
            try!(tree_file.write(FILE_HEADER.as_bytes()));

            // write out our version
            try!(tree_file.write(&[CURRENT_VERSION]));

            // construct and return our BTree object
            Ok(BTree{tree_file_path: tree_file_path,
                     tree_file: tree_file,
                     wal_file: wal_file,
                     root: None,
                     max_key_size: max_key_size,
                     max_value_size: max_value_size,
                     mem_tree: mem_tree
            })
        } else {
            let mut version_string = vec![0; 8];

            try!(tree_file.read_exact(&mut version_string));

            // make sure we've opened a proper file
            if try!(str::from_utf8(&version_string[0..FILE_HEADER.len()])) != FILE_HEADER ||
               version_string[FILE_HEADER.len()] != CURRENT_VERSION {
                return Err(From::from(std::io::Error::new(ErrorKind::InvalidData, "Invalid BTree file or BTree version")));
            }

            let mut buff = vec![0; node_size];

            // make sure we have a root node to read
            if metadata.len() < (version_string.len() + node_size) as u64 {
                // if we don't have a root node yet, just return
                return Ok(BTree{tree_file_path: tree_file_path,
                                tree_file: tree_file,
                                wal_file: wal_file,
                                root: None,
                                max_key_size: max_key_size,
                                max_value_size: max_value_size,
                                mem_tree: mem_tree
                });
            }

            // seek node_size in from the end of the file to read the root node
            try!(tree_file.seek(SeekFrom::End((node_size as isize * -1) as i64)));
            try!(tree_file.read_exact(&mut buff));

            let root_node: Node<K,V> = try!(decode(&buff[..]));

            Ok(BTree{tree_file_path: tree_file_path,
                     tree_file: tree_file,
                     wal_file: wal_file,
                     root: Some(root_node),
                     max_key_size: max_key_size,
                     max_value_size: max_value_size,
                     mem_tree: mem_tree
            })
        }
    }

    /// Inserts a key into the BTree
    pub fn insert(&mut self, key: K, value: V) -> Result<(), Box<Error>> {
        let record = KeyValuePair{key: key, value: value};

        try!(self.wal_file.write_record(&record));

        let KeyValuePair{key, value} = record;

        self.mem_tree.entry(key).or_insert(BTreeSet::<V>::new()).insert(value);

        Ok( () )
    }

/*
    /// Merges the records on disk with the records in memory
    fn compact(&mut self) -> Result<(), Box<Error>>{
        let mut new_tree_file = try!(OpenOptions::new().read(true).write(true).create(true).truncate(true).open(self.tree_file_path + ".new"));

        let mut mem_iter = self.mem_tree.iter().fuse();  // get an iterator that always returns None when done

        loop {
            let mem_item = mem_iter.next();

        }
    }
*/
}

#[cfg(test)]
mod tests {
    use std::fs;
    use std::fs::{OpenOptions, Metadata};
    use ::BTree;
    use rand::{thread_rng, Rng};

    pub fn gen_temp_name() -> String {
        let file_name: String = thread_rng().gen_ascii_chars().take(10).collect();

        return String::from("/tmp/") + &file_name + &String::from(".btr");
    }

    fn remove_files(file_path: String) {
        fs::remove_file(&file_path);
        fs::remove_file(file_path + ".wal");
    }

    #[test]
    fn new_blank_file() {
        let file_path = gen_temp_name();

        BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        // make sure our two files were created
        let btf = OpenOptions::new().read(true).write(false).create(false).open(&file_path).unwrap();
        assert!(btf.metadata().unwrap().len() == 8);

        let wal = OpenOptions::new().read(true).write(false).create(false).open(file_path.to_owned() + ".wal").unwrap();
        assert!(wal.metadata().unwrap().len() == 0);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn new_existing_file() {
        let file_path = gen_temp_name();

        {
            BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();
        }

        let btree = BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        // check our file lengths from the struct
        assert!(btree.tree_file.metadata().unwrap().len() == 8);
        assert!(btree.wal_file.len().unwrap() == 0);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_new_u8() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        let len = btree.insert(2, 3).unwrap(); // insert into a new file

        assert!(btree.wal_file.len().unwrap() == 2);
        assert!(btree.mem_tree.contains_key(&2));

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_new_str() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // insert into a new file
        btree.insert("Hello".to_owned(), "World".to_owned()).unwrap();

        assert!(! btree.wal_file.is_new().unwrap());
        assert!(btree.mem_tree.contains_key(&String::from("Hello")));

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_multiple() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // insert into a new file
        btree.insert("Hello".to_owned(), "World".to_owned()).unwrap();
        assert!(! btree.wal_file.is_new().unwrap());

        btree.insert("Hello".to_owned(), "Everyone".to_owned()).unwrap();
        assert!(! btree.wal_file.is_new().unwrap());

        remove_files(file_path); // remove files assuming it all went well
    }
}
<commit_msg>add get<commit_after>extern crate bincode;
extern crate rustc_serialize;
extern crate rand;

mod wal_file;
mod multi_map;

use wal_file::{KeyValuePair, WALFile, WALIterator};
// use multi_map::{MultiMap, MultiMapIterator};

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, decode};
use rustc_serialize::{Encodable, Decodable};

use std::cmp::max;
use std::convert::From;
use std::collections::{BTreeMap, BTreeSet};
use std::error::Error;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::{Read, Write, Seek, SeekFrom, ErrorKind};
use std::mem::{size_of};
use std::str;

const NUM_CHILDREN: usize = 32;
const FILE_HEADER: &'static str = "B+Tree\0";
const CURRENT_VERSION: u8 = 0x01;

// specify the types for the keys & values
pub trait KeyType: Ord + Encodable + Decodable + Clone {}
pub trait ValueType: Ord + Encodable + Decodable + Clone  {}

// provide generic implementations

impl<T> KeyType for T where T: Ord + Encodable + Decodable + Clone {}
impl<T> ValueType for T where T: Ord + Encodable + Decodable + Clone {}

#[derive(RustcEncodable, RustcDecodable, PartialEq)]
enum Payload<K: KeyType, V: ValueType> {
        Value(V),
        Children([(K,u64); NUM_CHILDREN]),
    }

#[derive(RustcEncodable, RustcDecodable, PartialEq)]
struct Node<K: KeyType, V: ValueType> {
    key: K,
    parent: u64,
    payload: Payload<K,V>, // either children, or actual values
}

/// This struct represents an on-disk B+Tree. There are NUM_CHILDREN keys at each
/// level in the tree. The on-disk format is as follows where VV is the version
/// number:
/// |-------------------------------------------|
/// | 0x42 0x2b 0x54 0x72 | 0x65 0x65 0x00 0xVV |
/// | B    +    T    r    | e    e    \0   0xVV |
/// |-------------------------------------------|
/// | smallest record in bincode format         |
/// |-------------------------------------------|
/// | ...                                       |
/// |-------------------------------------------|
/// | largest record in bincode format          |
/// |-------------------------------------------|
/// | internal nodes ...                        |
/// |-------------------------------------------|
/// | root node                                 |
/// |-------------------------------------------|
pub struct BTree<K: KeyType, V: ValueType> {
    tree_file_path: String,         // the path to the tree file
    tree_file: File,                // the file backing the whole thing
    wal_file: WALFile<K,V>,              // write-ahead log for in-memory items
    root: Option<Node<K,V>>,        // optional in-memory copy of the root node
    max_key_size: usize,            // the size of the key in bytes
    max_value_size: usize,          // the size of the value in bytes
    mem_tree: BTreeMap<K, BTreeSet<V>>,  // the in-memory BTree that gets merged with the on-disk one
}

impl <K: KeyType, V: ValueType> BTree<K, V> {
    pub fn new(tree_file_path: String, max_key_size: usize, max_value_size: usize) -> Result<BTree<K,V>, Box<Error>> {
        // create our mem_tree
        let mut mem_tree = BTreeMap::<K, BTreeSet<V>>::new();

        let wal_file_path = tree_file_path.to_owned() + ".wal";

        let mut wal_file = try!(WALFile::<K,V>::new(wal_file_path.to_owned(), max_key_size, max_value_size));

        let record_size = max_key_size + max_value_size;

        // if we have a WAL file, replay it into the mem_tree
        if try!(wal_file.is_new()) {
            for kv in &mut wal_file {
                mem_tree.entry(kv.key).or_insert(BTreeSet::<V>::new()).insert(kv.value);
            }
        }

        // compute the size of a on-disk Node
        let node_size: usize = (max_key_size + size_of::<u64>() + max(max_value_size, (max_key_size + size_of::<u64>()) * NUM_CHILDREN)) as usize;

        // open the data file
        let mut tree_file = try!(OpenOptions::new().read(true).write(true).create(true).open(tree_file_path.to_owned()));

        let metadata = try!(tree_file.metadata());

        // check to see if this is a new file
        if metadata.len() == 0 {
            // write out our header
            try!(tree_file.write(FILE_HEADER.as_bytes()));

            // write out our version
            try!(tree_file.write(&[CURRENT_VERSION]));

            // construct and return our BTree object
            Ok(BTree{tree_file_path: tree_file_path,
                     tree_file: tree_file,
                     wal_file: wal_file,
                     root: None,
                     max_key_size: max_key_size,
                     max_value_size: max_value_size,
                     mem_tree: mem_tree
            })
        } else {
            let mut version_string = vec![0; 8];

            try!(tree_file.read_exact(&mut version_string));

            // make sure we've opened a proper file
            if try!(str::from_utf8(&version_string[0..FILE_HEADER.len()])) != FILE_HEADER ||
               version_string[FILE_HEADER.len()] != CURRENT_VERSION {
                return Err(From::from(std::io::Error::new(ErrorKind::InvalidData, "Invalid BTree file or BTree version")));
            }

            let mut buff = vec![0; node_size];

            // make sure we have a root node to read
            if metadata.len() < (version_string.len() + node_size) as u64 {
                // if we don't have a root node yet, just return
                return Ok(BTree{tree_file_path: tree_file_path,
                                tree_file: tree_file,
                                wal_file: wal_file,
                                root: None,
                                max_key_size: max_key_size,
                                max_value_size: max_value_size,
                                mem_tree: mem_tree
                });
            }

            // seek node_size in from the end of the file to read the root node
            try!(tree_file.seek(SeekFrom::End((node_size as isize * -1) as i64)));
            try!(tree_file.read_exact(&mut buff));

            let root_node: Node<K,V> = try!(decode(&buff[..]));

            Ok(BTree{tree_file_path: tree_file_path,
                     tree_file: tree_file,
                     wal_file: wal_file,
                     root: Some(root_node),
                     max_key_size: max_key_size,
                     max_value_size: max_value_size,
                     mem_tree: mem_tree
            })
        }
    }

    /// Inserts a key into the BTree
    pub fn insert(&mut self, key: K, value: V) -> Result<(), Box<Error>> {
        let record = KeyValuePair{key: key, value: value};

        try!(self.wal_file.write_record(&record));

        let KeyValuePair{key, value} = record;

        self.mem_tree.entry(key).or_insert(BTreeSet::<V>::new()).insert(value);

        Ok( () )
    }

    pub fn get(&self, key: &K) -> Option<&BTreeSet<V>> {
        self.mem_tree.get(key)
    }
/*
    /// Merges the records on disk with the records in memory
    fn compact(&mut self) -> Result<(), Box<Error>>{
        let mut new_tree_file = try!(OpenOptions::new().read(true).write(true).create(true).truncate(true).open(self.tree_file_path + ".new"));

        let mut mem_iter = self.mem_tree.iter().fuse();  // get an iterator that always returns None when done

        loop {
            let mem_item = mem_iter.next();

        }
    }
*/
}

#[cfg(test)]
mod tests {
    use std::fs;
    use std::fs::{OpenOptions, Metadata};
    use ::BTree;
    use rand::{thread_rng, Rng};
    use std::collections::{BTreeSet};

    pub fn gen_temp_name() -> String {
        let file_name: String = thread_rng().gen_ascii_chars().take(10).collect();

        return String::from("/tmp/") + &file_name + &String::from(".btr");
    }

    fn remove_files(file_path: String) {
        fs::remove_file(&file_path);
        fs::remove_file(file_path + ".wal");
    }

    #[test]
    fn new_blank_file() {
        let file_path = gen_temp_name();

        BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        // make sure our two files were created
        let btf = OpenOptions::new().read(true).write(false).create(false).open(&file_path).unwrap();
        assert!(btf.metadata().unwrap().len() == 8);

        let wal = OpenOptions::new().read(true).write(false).create(false).open(file_path.to_owned() + ".wal").unwrap();
        assert!(wal.metadata().unwrap().len() == 0);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn new_existing_file() {
        let file_path = gen_temp_name();

        {
            BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();
        }

        let btree = BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        // check our file lengths from the struct
        assert!(btree.tree_file.metadata().unwrap().len() == 8);
        assert!(btree.wal_file.len().unwrap() == 0);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_new_u8() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        let len = btree.insert(2, 3).unwrap(); // insert into a new file

        assert!(btree.wal_file.len().unwrap() == 2);
        assert!(btree.mem_tree.contains_key(&2));

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_new_str() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // insert into a new file
        btree.insert("Hello".to_owned(), "World".to_owned()).unwrap();

        assert!(! btree.wal_file.is_new().unwrap());
        assert!(btree.mem_tree.contains_key(&String::from("Hello")));

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn get_returns_a_set() {
        let file_path = gen_temp_name();

        // setup tree
        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // expected return set
        let mut expected: BTreeSet<String> = BTreeSet::new();
        expected.insert("World".to_string());

        btree.insert("Hello".to_owned(), "World".to_owned());

        // get the set at the hello key
        let set_at_hello: &BTreeSet<String> = btree.get(&"Hello".to_string()).unwrap();

        assert_eq!(&expected, set_at_hello);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_multiple() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // insert into a new file
        btree.insert("Hello".to_owned(), "World".to_owned()).unwrap();
        assert!(! btree.wal_file.is_new().unwrap());

        btree.insert("Hello".to_owned(), "Everyone".to_owned()).unwrap();
        assert!(! btree.wal_file.is_new().unwrap());

        remove_files(file_path); // remove files assuming it all went well
    }
}
<|endoftext|>


<commit_before>static LOOKUP: [(usize, &'static str); 13] = [(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),
                                              (100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
                                              (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")];

pub fn to_arabic(roman: &str) -> Option<usize> {

    let iter = roman
              .chars()
              .map(roman_char_to_arabic_digit);

    let mut num = 0;
    let mut prev = 0;

    for d in iter {

        if d <= 0 {
            return None;
        }

        num += d;

        if prev != 0 && d > prev {
            num -= prev * 2;
        }

        prev = d;
    }

    Some(num)
}

fn roman_char_to_arabic_digit(roman: char) -> usize {

    let mut i = 0;

    for &(n, s) in &LOOKUP {
        if i % 2 == 0 && s.chars().nth(0) == roman.to_uppercase().next() {
            return n;
        }
        i+=1;
    }

    0
}

pub fn to_roman(number: usize) -> Option<String> {

    if number <= 0 || number >= 5000 {
        return None;
    }

    let mut roman = String::new();
    let mut num = number;

    while num > 0 {
        for &(n, s) in &LOOKUP {
            if n <= num {
                roman.push_str(s);
                num-= n;
                break;
            }
        }
    }

    Some(roman)
}

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

    #[test]
    fn test_to_arabic() {
        assert_eq!(to_arabic("CliiI"), Some(153));
        assert_eq!(to_arabic("XxXiV"), Some(34));
        assert_eq!(to_arabic("CM"), Some(900));
        assert_eq!(to_arabic("ABC0"), None);
        assert_eq!(to_arabic("vii"), Some(7));
        assert_eq!(to_arabic("IC"), Some(99));
        assert_eq!(to_arabic("XCIX"), Some(99));
        assert_eq!(to_arabic("XA"), None);
    }

    #[test]
    fn test_to_roman() {
        assert_eq!(to_roman(5), Some("V".to_string()));
        assert_eq!(to_roman(101), Some("CI".to_string()));
        assert_eq!(to_roman(99), Some("XCIX".to_string()));
        assert_eq!(to_roman(4999), Some("MMMMCMXCIX".to_string()));
        assert_eq!(to_roman(5000), None);
        assert_eq!(to_roman(5001), None);
        assert_eq!(to_roman(10000), None);
        assert_eq!(to_roman(0), None);
    }

}<commit_msg>Updated range checking<commit_after>static LOOKUP: [(usize, &'static str); 13] = [(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),
                                              (100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
                                              (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")];

pub fn to_arabic(roman: &str) -> Option<usize> {

    let iter = roman
              .chars()
              .map(roman_char_to_arabic_digit);

    let mut num = 0;
    let mut prev = 0;

    for d in iter {

        if d <= 0 {
            return None;
        }

        num += d;

        if prev != 0 && d > prev {
            num -= prev * 2;
        }

        prev = d;
    }

    Some(num)
}

fn roman_char_to_arabic_digit(roman: char) -> usize {

    let mut i = 0;

    for &(n, s) in &LOOKUP {
        if i % 2 == 0 && s.chars().nth(0) == roman.to_uppercase().next() {
            return n;
        }
        i+=1;
    }

    0
}

pub fn to_roman(number: usize) -> Option<String> {

    if number < 1 || number >= 5000 {
        return None;
    }

    let mut roman = String::new();
    let mut num = number;

    while num > 0 {
        for &(n, s) in &LOOKUP {
            if n <= num {
                roman.push_str(s);
                num-= n;
                break;
            }
        }
    }

    Some(roman)
}

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

    #[test]
    fn test_to_arabic() {
        assert_eq!(to_arabic("CliiI"), Some(153));
        assert_eq!(to_arabic("XxXiV"), Some(34));
        assert_eq!(to_arabic("CM"), Some(900));
        assert_eq!(to_arabic("ABC0"), None);
        assert_eq!(to_arabic("vii"), Some(7));
        assert_eq!(to_arabic("IC"), Some(99));
        assert_eq!(to_arabic("XCIX"), Some(99));
        assert_eq!(to_arabic("XA"), None);
    }

    #[test]
    fn test_to_roman() {
        assert_eq!(to_roman(5), Some("V".to_string()));
        assert_eq!(to_roman(101), Some("CI".to_string()));
        assert_eq!(to_roman(99), Some("XCIX".to_string()));
        assert_eq!(to_roman(4999), Some("MMMMCMXCIX".to_string()));
        assert_eq!(to_roman(5000), None);
        assert_eq!(to_roman(5001), None);
        assert_eq!(to_roman(10000), None);
        assert_eq!(to_roman(0), None);
    }

}<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::io::{IoError, IoErrorKind, Reader};

struct Frame;

// TODO: there should be a way to signal IO error.
// This will be handled by the error reform RFCs, I think.
// For now, use IO error to indicate any failure.
pub type Error = IoError;

fn mk_err() -> Error {
    IoError {
        kind: IoErrorKind::OtherIoError,
        desc: "FLAC decoding error",
        detail: None
    }
}

struct FlacStream {
    metadata_blocks: Vec<MetadataBlock>
}

#[cfg(test)]
fn get_foo_file() -> File {
    File::open(&Path::new("foo.flac")).unwrap()
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_stream_header(input: &mut Reader) -> Result<(), Error> {
    // A FLAC stream starts with a 32-bit header 'fLaC' (big endian).
    const HEADER: u32 = 0x66_4c_61_43;
    let header = try!(input.read_be_u32());
    if header != HEADER { return Err(mk_err()); } // TODO: provide more error info.
    Ok(())
}

#[test]
fn test_read_stream_header() {
    let mut input = get_foo_file();
    read_stream_header(&mut input).unwrap();
}

// TODO: should this be private or not?
#[deriving(Show)]
pub struct StreamInfo {
    pub min_block_size: u16,
    pub max_block_size: u16,
    pub min_frame_size: Option<u32>,
    pub max_frame_size: Option<u32>,
    pub sample_rate: u32,
    pub n_channels: u8,
    pub bits_per_sample: u8,
    pub n_samples: Option<u64>,
    pub md5sum: [u8, ..16]
}

// TODO: should this be private?
pub struct SeekPoint {
    pub sample: u64,
    pub offset: u64,
    pub n_samples: u16
}

// TODO: should this be private?
pub struct SeekTable {
    seekpoints: Vec<SeekPoint>
}

pub enum MetadataBlock {
    StreamInfo(StreamInfo),
    Padding { length: u32 },
    Application { id: u32, data: Vec<u8> },
    SeekTable(SeekTable),
    VorbisComment, // TODO
    CueSheet, // TODO
    Picture // TODO
}

// TODO: this should be private.
pub struct MetadataBlockHeader {
    is_last: bool,
    block_type: u8,
    length: u32
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_metadata_block_header(input: &mut Reader) -> Result<MetadataBlockHeader, Error> {
    let byte = try!(input.read_u8());

    // The first bit specifies whether this is the last block, the next 7 bits
    // specify the type of the metadata block to follow.
    let is_last = (byte & 1) == 1;
    let block_type = byte >> 1;

    if block_type == 127 { return Err(mk_err()); } // TODO: "invalid, to avoid confusion with a frame sync code"
    if block_type > 6 { return Err(mk_err()); } // TODO: "reserved"

    // The length field is 24 bits, or 3 bytes.
    let length = try!(input.read_be_uint_n(3)) as u32;

    let header = MetadataBlockHeader {
        is_last: is_last,
        block_type: block_type,
        length: length
    };
    Ok(header)
}

fn read_metadata_block(input: &mut Reader, block_type: u8, length: u32)
                       -> Result<MetadataBlock, Error> {
    let block = match block_type {
        0 => {
            let streaminfo = try!(read_streaminfo_block(input));
            MetadataBlock::StreamInfo(streaminfo)
        },
        1 => {
            try!(read_padding_block(input, length));
            MetadataBlock::Padding { length: length }
        },
        2 => {
            let (id, data) = try!(read_application_block(input, length));
            MetadataBlock::Application { id: id, data: data }
        },
        _ => return Err(mk_err())
    };

    Ok(block)
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_streaminfo_block(input: &mut Reader) -> Result<StreamInfo, Error> {
    let min_block_size = try!(input.read_be_u16());
    let max_block_size = try!(input.read_be_u16());

    // The frame size fields are 24 bits, or 3 bytes.
    let min_frame_size = try!(input.read_be_uint_n(3)) as u32;
    let max_frame_size = try!(input.read_be_uint_n(3)) as u32;

    // Next up are 20 bits that determine the sample rate.
    let sample_rate_msb = try!(input.read_be_u16());
    let sample_rate_lsb = try!(input.read_byte());

    // Stitch together the value from the first 16 bits,
    // and then the 4 most significant bits of the next byte.
    let sample_rate = sample_rate_msb as u32 << 4 | sample_rate_lsb as u32 >> 4;

    // Next three bits are the number of channels - 1. Mask them out and add 1.
    let n_channels_bps = sample_rate_lsb;
    let n_channels = ((n_channels_bps >> 1) & 0x7) + 1;

    // The final bit is the most significant of bits per sample - 1. Bits per
    // sample - 1 is 5 bits in total.
    let bps_msb = n_channels_bps & 1;
    let bps_lsb_n_samples = try!(input.read_byte());

    // Stitch together these values, add 1 because # - 1 is stored.
    let bits_per_sample = (bps_msb << 4 | (bps_lsb_n_samples >> 4)) + 1;

    // Number of samples in 36 bits, we have 4 already, 32 to go.
    let n_samples_msb = bps_lsb_n_samples & 0xf;
    let n_samples_lsb = try!(input.read_be_u32());
    let n_samples = n_samples_msb as u64 << 32 | n_samples_lsb as u64;

    let mut md5sum = [0u8, ..16];
    try!(input.read_at_least(16, &mut md5sum));

    let stream_info = StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: if min_frame_size == 0 { None } else { Some(min_frame_size) },
        max_frame_size: if max_frame_size == 0 { None } else { Some(max_frame_size) },
        sample_rate: sample_rate,
        n_channels: n_channels,
        bits_per_sample: bits_per_sample,
        n_samples: if n_samples == 0 { None } else { Some(n_samples) },
        md5sum: md5sum
    };

    Ok(stream_info)
}

fn read_padding_block(input: &mut Reader, length: u32) -> Result<(), Error> {
    // Skip all the padding bytes.
    for _ in range(0, length) { try!(input.read_byte()); }
    Ok(())
}

fn read_application_block(input: &mut Reader, length: u32) -> Result<(u32, Vec<u8>), Error> {
    let id = try!(input.read_be_u32());

    // Four bytes of the block have been used for the ID, the rest is payload.
    let data = try!(input.read_exact((length - 4) as uint));

    Ok((id, data))
}

impl FlacStream {
    pub fn new(input: &mut Reader) -> Result<FlacStream, Error> {
        try!(read_stream_header(input));

        let hdr = try!(read_metadata_block_header(input));
        println!("type: {}, length: {}, last: {}",
                 hdr.block_type, hdr.length, hdr.is_last);

        let streaminfo = try!(read_streaminfo_block(input));
        println!("streaminfo: {}", streaminfo);
        // Read the STREAMINFO block
        // Read any metadata
        // Read frames

        let flac_stream = FlacStream {
            metadata_blocks: Vec::new()
        };

        Ok(flac_stream)
    }
}

#[test]
fn test_open_stream() {
    let mut input = get_foo_file();
    let flac_stream = FlacStream::new(&mut input).unwrap();
}
<commit_msg>skip several metadata blocks for now<commit_after>#![allow(dead_code)]

use std::io::{IoError, IoErrorKind, Reader};

struct Frame;

// TODO: there should be a way to signal IO error.
// This will be handled by the error reform RFCs, I think.
// For now, use IO error to indicate any failure.
pub type Error = IoError;

fn mk_err() -> Error {
    IoError {
        kind: IoErrorKind::OtherIoError,
        desc: "FLAC decoding error",
        detail: None
    }
}

struct FlacStream {
    metadata_blocks: Vec<MetadataBlock>
}

#[cfg(test)]
fn get_foo_file() -> File {
    File::open(&Path::new("foo.flac")).unwrap()
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_stream_header(input: &mut Reader) -> Result<(), Error> {
    // A FLAC stream starts with a 32-bit header 'fLaC' (big endian).
    const HEADER: u32 = 0x66_4c_61_43;
    let header = try!(input.read_be_u32());
    if header != HEADER { return Err(mk_err()); } // TODO: provide more error info.
    Ok(())
}

#[test]
fn test_read_stream_header() {
    let mut input = get_foo_file();
    read_stream_header(&mut input).unwrap();
}

// TODO: should this be private or not?
#[deriving(Show)]
pub struct StreamInfo {
    pub min_block_size: u16,
    pub max_block_size: u16,
    pub min_frame_size: Option<u32>,
    pub max_frame_size: Option<u32>,
    pub sample_rate: u32,
    pub n_channels: u8,
    pub bits_per_sample: u8,
    pub n_samples: Option<u64>,
    pub md5sum: [u8, ..16]
}

// TODO: should this be private?
pub struct SeekPoint {
    pub sample: u64,
    pub offset: u64,
    pub n_samples: u16
}

// TODO: should this be private?
pub struct SeekTable {
    seekpoints: Vec<SeekPoint>
}

pub enum MetadataBlock {
    StreamInfo(StreamInfo),
    Padding { length: u32 },
    Application { id: u32, data: Vec<u8> },
    SeekTable(SeekTable),
    VorbisComment, // TODO
    CueSheet, // TODO
    Picture // TODO
}

// TODO: this should be private.
pub struct MetadataBlockHeader {
    is_last: bool,
    block_type: u8,
    length: u32
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_metadata_block_header(input: &mut Reader) -> Result<MetadataBlockHeader, Error> {
    let byte = try!(input.read_u8());

    // The first bit specifies whether this is the last block, the next 7 bits
    // specify the type of the metadata block to follow.
    let is_last = (byte & 1) == 1;
    let block_type = byte >> 1;

    // The length field is 24 bits, or 3 bytes.
    let length = try!(input.read_be_uint_n(3)) as u32;

    let header = MetadataBlockHeader {
        is_last: is_last,
        block_type: block_type,
        length: length
    };
    Ok(header)
}

fn read_metadata_block(input: &mut Reader, block_type: u8, length: u32)
                       -> Result<MetadataBlock, Error> {
    match block_type {
        0 => {
            let streaminfo = try!(read_streaminfo_block(input));
            Ok(MetadataBlock::StreamInfo(streaminfo))
        },
        1 => {
            try!(read_padding_block(input, length));
            Ok(MetadataBlock::Padding { length: length })
        },
        2 => {
            let (id, data) = try!(read_application_block(input, length));
            Ok(MetadataBlock::Application { id: id, data: data })
        },
        3 => {
            // TODO: implement seektable reading. For now, pretend it is padding.
            try!(skip_block(input, length));
            Ok(MetadataBlock::Padding { length: length })
        },
        4 => {
            // TODO: implement Vorbis comment reading. For now, pretend it is padding.
            try!(skip_block(input, length));
            Ok(MetadataBlock::Padding { length: length })
        },
        5 => {
            // TODO: implement CUE sheet reading. For now, pretend it is padding.
            try!(skip_block(input, length));
            Ok(MetadataBlock::Padding { length: length })
        },
        6 => {
            // TODO: implement picture reading. For now, pretend it is padding.
            try!(skip_block(input, length));
            Ok(MetadataBlock::Padding { length: length })
        },
        127 => Err(mk_err()), // TODO: "invalid, to avoid confusion with a frame sync code"
        _ => Err(mk_err()) // TODO: "reserved", is that an error or do we ignore it?
    }
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_streaminfo_block(input: &mut Reader) -> Result<StreamInfo, Error> {
    let min_block_size = try!(input.read_be_u16());
    let max_block_size = try!(input.read_be_u16());

    // The frame size fields are 24 bits, or 3 bytes.
    let min_frame_size = try!(input.read_be_uint_n(3)) as u32;
    let max_frame_size = try!(input.read_be_uint_n(3)) as u32;

    // Next up are 20 bits that determine the sample rate.
    let sample_rate_msb = try!(input.read_be_u16());
    let sample_rate_lsb = try!(input.read_byte());

    // Stitch together the value from the first 16 bits,
    // and then the 4 most significant bits of the next byte.
    let sample_rate = sample_rate_msb as u32 << 4 | sample_rate_lsb as u32 >> 4;

    // Next three bits are the number of channels - 1. Mask them out and add 1.
    let n_channels_bps = sample_rate_lsb;
    let n_channels = ((n_channels_bps >> 1) & 0x7) + 1;

    // The final bit is the most significant of bits per sample - 1. Bits per
    // sample - 1 is 5 bits in total.
    let bps_msb = n_channels_bps & 1;
    let bps_lsb_n_samples = try!(input.read_byte());

    // Stitch together these values, add 1 because # - 1 is stored.
    let bits_per_sample = (bps_msb << 4 | (bps_lsb_n_samples >> 4)) + 1;

    // Number of samples in 36 bits, we have 4 already, 32 to go.
    let n_samples_msb = bps_lsb_n_samples & 0xf;
    let n_samples_lsb = try!(input.read_be_u32());
    let n_samples = n_samples_msb as u64 << 32 | n_samples_lsb as u64;

    let mut md5sum = [0u8, ..16];
    try!(input.read_at_least(16, &mut md5sum));

    let stream_info = StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: if min_frame_size == 0 { None } else { Some(min_frame_size) },
        max_frame_size: if max_frame_size == 0 { None } else { Some(max_frame_size) },
        sample_rate: sample_rate,
        n_channels: n_channels,
        bits_per_sample: bits_per_sample,
        n_samples: if n_samples == 0 { None } else { Some(n_samples) },
        md5sum: md5sum
    };

    Ok(stream_info)
}

fn read_padding_block(input: &mut Reader, length: u32) -> Result<(), Error> {
    // The specification dictates that all bits of the padding block must be 0.
    for _ in range(0, length) {
        if try!(input.read_byte()) != 0 {
            return Err(mk_err()); // TODO: error, nonzero bit in padding block.
        }
    }
    Ok(())
}

fn skip_block(input: &mut Reader, length: u32) -> Result<(), Error> {
    // Skip all the padding bytes.
    for _ in range(0, length) { try!(input.read_byte()); }
    Ok(())
}

fn read_application_block(input: &mut Reader, length: u32) -> Result<(u32, Vec<u8>), Error> {
    let id = try!(input.read_be_u32());

    // Four bytes of the block have been used for the ID, the rest is payload.
    let data = try!(input.read_exact((length - 4) as uint));

    Ok((id, data))
}

impl FlacStream {
    pub fn new(input: &mut Reader) -> Result<FlacStream, Error> {
        try!(read_stream_header(input));

        let hdr = try!(read_metadata_block_header(input));
        println!("type: {}, length: {}, last: {}",
                 hdr.block_type, hdr.length, hdr.is_last);

        let streaminfo = try!(read_streaminfo_block(input));
        println!("streaminfo: {}", streaminfo);
        // Read the STREAMINFO block
        // Read any metadata
        // Read frames

        let flac_stream = FlacStream {
            metadata_blocks: Vec::new()
        };

        Ok(flac_stream)
    }
}

#[test]
fn test_open_stream() {
    let mut input = get_foo_file();
    let flac_stream = FlacStream::new(&mut input).unwrap();
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Implement basic Rotor type with substitutions<commit_after>use std::ascii::AsciiExt;

struct Rotor<'a> {
    rotor: &'a [u8]
}

impl<'a> Rotor<'a> {
    pub fn new(rotor: &'a str) -> Rotor<'a> {
        Rotor {
            rotor: rotor.as_bytes(),
        }
    }

    fn substitute(&self, c: char) -> char {
        if !c.is_ascii() || !c.is_alphabetic() {
            return c;
        }

        let letter = c.to_ascii_uppercase();
        let offset = ((letter as u8) - ('A' as u8)) as usize;

        self.rotor[offset] as char
    }
}

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

    #[test]
    fn char_substitution() {
        let rotor = Rotor::new("XYZ");
        assert!(rotor.substitute('A') == 'X');
        assert!(rotor.substitute('b') == 'Y');
        assert!(rotor.substitute('!') == '!');
        assert!(rotor.substitute('é') == 'é');
    }
}
<|endoftext|>


<commit_before>extern crate petgraph;

// XXX: Self-loops?

use petgraph::{Graph, Directed};
use petgraph::graph::{NodeIndex, IndexType};
use std::collections::HashSet;

#[repr(u8)]
#[derive(Debug)]
pub enum TriadType {
    T003 = 0,
    T012,
    T102,
    T021D,

    T021U,
    T021C,
    T111D,
    T111U,

    T030T,
    T030C,
    T201,
    T120D,

    T120U,
    T120C,
    T210,
    T300,
}

impl TriadType {
    #[inline(always)]
    fn from_u8(i: u8) -> TriadType {
        use std::mem;
        assert!(i < 16);
        unsafe { mem::transmute(i) }
    }
}

pub type NodeIdx = usize;

/// Interface required for Triadic Census.
/// Nodes are externally represented by `NodeIdx`.
pub trait DirectedGraph {
    fn node_count(&self) -> NodeIdx;
    fn has_edge(&self, src: NodeIdx, dst: NodeIdx) -> bool;
    fn each_undirected_neighbor<F:FnMut(NodeIdx)>(&self, node: NodeIdx, callback: F);
}

fn tricode<G: DirectedGraph>(graph: &G,
                               v: NodeIdx,
                               u: NodeIdx,
                               w: NodeIdx)
                               -> usize {
    let mut tricode = 0;
    if graph.has_edge(v, u) {
        tricode |= 1 << 0;
    }
    if graph.has_edge(u, v) {
        tricode |= 1 << 1;
    }
    if graph.has_edge(v, w) {
        tricode |= 1 << 2;
    }
    if graph.has_edge(w, v) {
        tricode |= 1 << 3;
    }
    if graph.has_edge(u, w) {
        tricode |= 1 << 4;
    }
    if graph.has_edge(w, u) {
        tricode |= 1 << 5;
    }
    return tricode;
}

const TRITYPES: [u8; 64] = [0, 1, 1, 2, 1, 3, 5, 7, 1, 5, 4, 6, 2, 7, 6, 10, 1, 5, 3, 7, 4, 8, 8,
                            12, 5, 9, 8, 13, 6, 13, 11, 14, 1, 4, 5, 6, 5, 8, 9, 13, 3, 8, 8, 11,
                            7, 12, 13, 14, 2, 6, 7, 10, 6, 11, 13, 14, 7, 13, 12, 14, 10, 14, 14,
                            15];

#[derive(Debug)]
pub struct TriadicCensus {
    census: [u64; 16],
}

impl TriadicCensus {
    /// Calculates the `distance` between two TriadicCensus structs, treated as
    /// points in a 16-dimensional space.
    pub fn distance(a: &TriadicCensus, b: &TriadicCensus) -> f64 {
        let mut sum = 0.0f64;
        for i in 0..16 {
            let va = a.census[i];
            let vb = b.census[i];
            let diff = (if va > vb {
                va - vb
            } else {
                vb - va
            }) as f64;
            sum += diff * diff;
        }
        sum.sqrt()
    }

    #[inline(always)]
    fn new() -> TriadicCensus {
        TriadicCensus { census: [0; 16] }
    }

    #[inline(always)]
    fn add(&mut self, triad_type: TriadType, cnt: u64) {
        self.census[triad_type as usize] += cnt;
    }

    #[inline(always)]
    fn set(&mut self, triad_type: TriadType, cnt: u64) {
        self.census[triad_type as usize] = cnt;
    }

    #[inline(always)]
    pub fn get(&self, triad_type: TriadType) -> u64 {
        self.census[triad_type as usize]
    }

    #[inline(always)]
    pub fn as_slice<'a>(&'a self) -> &'a [u64] {
        &self.census[..]
    }

    /// XXX: Optimize: find_edge() is O(k) with k the number of edges.
    pub fn from_graph<G: DirectedGraph>(graph: &G) -> TriadicCensus {
        let n = graph.node_count();

        let mut census = TriadicCensus::new();

        for v in 0..n {
            let mut neighbors_v = HashSet::new();
            graph.each_undirected_neighbor(v, |u| if u > v { neighbors_v.insert(u); });

            for u in neighbors_v {
                let mut s = HashSet::new(); // XXX: Use bitset? // reuse
                graph.each_undirected_neighbor(u, |nu| {s.insert(nu);});
                graph.each_undirected_neighbor(v, |nv| {s.insert(nv);});
                let _ = s.remove(&u);
                let _ = s.remove(&v);

                let tri_type = if graph.has_edge(v, u) && graph.has_edge(u, v) {
                    TriadType::T102
                } else {
                    TriadType::T012
                };

                let cnt = n as i64 - s.len() as i64 - 2;
                assert!(cnt >= 0);
                census.add(tri_type, cnt as u64);
                for w in s {
                    if u < w ||
                       (v < w && w < u &&
                        !(graph.has_edge(v, w) || graph.has_edge(w, v))) {
                        let tri_type = TriadType::from_u8(TRITYPES[tricode(graph, v, u, w)]);
                        census.add(tri_type, 1);
                    }
                }
            }
        }

        let mut sum = 0;
        for &cnt in &census.as_slice()[1..] {
            sum += cnt;
        }
        let n = n as u64;
        // Integer division below is guaranteed to produce an integral result
        census.set(TriadType::T003, (n * (n - 1) * (n - 2)) / 6 - sum);
        return census;
    }
}

pub struct SimpleDiGraph {
    g: Graph<(), (), Directed>
}

impl SimpleDiGraph {
    pub fn new() -> SimpleDiGraph {
        SimpleDiGraph {g: Graph::new()}
    }

    pub fn add_node(&mut self) -> NodeIdx {
        self.g.add_node(()).index()
    }

    pub fn add_edge(&mut self, src: NodeIdx, dst: NodeIdx) {
        self.g.add_edge(NodeIndex::new(src), NodeIndex::new(dst), ());
    }
}

impl DirectedGraph for SimpleDiGraph {
    fn node_count(&self) -> NodeIdx {
        self.g.node_count()
    }

    #[inline]
    fn has_edge(&self, src: NodeIdx, dst: NodeIdx) -> bool {
        self.g.find_edge(NodeIndex::new(src), NodeIndex::new(dst)).is_some()
    }

    #[inline]
    fn each_undirected_neighbor<F:FnMut(NodeIdx)>(&self, node: NodeIdx, mut callback: F) {
        for n in self.g.neighbors_undirected(NodeIndex::new(node)) {
            callback(n.index())
        }
    }
}

#[test]
fn test_simple() {
    let mut g = SimpleDiGraph::new();

    let n1 = g.add_node();
    let n2 = g.add_node();
    let n3 = g.add_node();

    let c1 = TriadicCensus::from_graph(&g);
    assert_eq!(&[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c1.as_slice());

    g.add_edge(n1, n3);
    let c2 = TriadicCensus::from_graph(&g);
    assert_eq!(&[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c2.as_slice());

    let d = TriadicCensus::distance(&c1, &c2); // should be sqrt(2)
    assert!(d > 1.414 && d < 1.415);

    g.add_edge(n3, n1);
    let c3 = TriadicCensus::from_graph(&g);
    assert_eq!(&[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c3.as_slice());

    g.add_edge(n2, n3);
    let c4 = TriadicCensus::from_graph(&g);
    assert_eq!(&[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c4.as_slice());
}

#[cfg(test)]
fn line_graph(n: u32) -> SimpleDiGraph {
    let mut g = SimpleDiGraph::new();

    let mut prev = g.add_node();
    for _ in 0..n - 1 {
        let cur = g.add_node();
        g.add_edge(prev, cur);
        prev = cur;
    }
    return g;
}

#[cfg(test)]
fn circular_graph(n: u32) -> SimpleDiGraph {
    let mut g = SimpleDiGraph::new();

    let first = g.add_node();
    let mut prev = first;
    for _ in 0..n - 1 {
        let cur = g.add_node();
        g.add_edge(prev, cur);
        prev = cur;
    }
    g.add_edge(prev, first);
    return g;
}

#[test]
fn test_line_graph() {
    // Result compared with igraph.triad_census().
    let c = TriadicCensus::from_graph(&line_graph(20));
    assert_eq!(&[816, 306, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c.as_slice());

    let c = TriadicCensus::from_graph(&line_graph(40));
    assert_eq!(&[8436, 1406, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c.as_slice());
}

#[test]
fn test_circular_graph() {
    // Result compared with igraph.triad_census().
    let c = TriadicCensus::from_graph(&circular_graph(20));
    assert_eq!(&[800, 320, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c.as_slice());

    let c = TriadicCensus::from_graph(&circular_graph(40));
    assert_eq!(&[8400, 1440, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c.as_slice());

}
<commit_msg>Move use to front<commit_after>extern crate petgraph;

// XXX: Self-loops?

use petgraph::{Graph, Directed};
use petgraph::graph::{NodeIndex, IndexType};
use std::collections::HashSet;
use std::mem;

#[repr(u8)]
#[derive(Debug)]
pub enum TriadType {
    T003 = 0,
    T012,
    T102,
    T021D,

    T021U,
    T021C,
    T111D,
    T111U,

    T030T,
    T030C,
    T201,
    T120D,

    T120U,
    T120C,
    T210,
    T300,
}

impl TriadType {
    #[inline(always)]
    fn from_u8(i: u8) -> TriadType {
        assert!(i < 16);
        unsafe { mem::transmute(i) }
    }
}

pub type NodeIdx = usize;

/// Interface required for Triadic Census.
/// Nodes are externally represented by `NodeIdx`.
pub trait DirectedGraph {
    fn node_count(&self) -> NodeIdx;
    fn has_edge(&self, src: NodeIdx, dst: NodeIdx) -> bool;
    fn each_undirected_neighbor<F:FnMut(NodeIdx)>(&self, node: NodeIdx, callback: F);
}

fn tricode<G: DirectedGraph>(graph: &G,
                               v: NodeIdx,
                               u: NodeIdx,
                               w: NodeIdx)
                               -> usize {
    let mut tricode = 0;
    if graph.has_edge(v, u) {
        tricode |= 1 << 0;
    }
    if graph.has_edge(u, v) {
        tricode |= 1 << 1;
    }
    if graph.has_edge(v, w) {
        tricode |= 1 << 2;
    }
    if graph.has_edge(w, v) {
        tricode |= 1 << 3;
    }
    if graph.has_edge(u, w) {
        tricode |= 1 << 4;
    }
    if graph.has_edge(w, u) {
        tricode |= 1 << 5;
    }
    return tricode;
}

const TRITYPES: [u8; 64] = [0, 1, 1, 2, 1, 3, 5, 7, 1, 5, 4, 6, 2, 7, 6, 10, 1, 5, 3, 7, 4, 8, 8,
                            12, 5, 9, 8, 13, 6, 13, 11, 14, 1, 4, 5, 6, 5, 8, 9, 13, 3, 8, 8, 11,
                            7, 12, 13, 14, 2, 6, 7, 10, 6, 11, 13, 14, 7, 13, 12, 14, 10, 14, 14,
                            15];

#[derive(Debug)]
pub struct TriadicCensus {
    census: [u64; 16],
}

impl TriadicCensus {
    /// Calculates the `distance` between two TriadicCensus structs, treated as
    /// points in a 16-dimensional space.
    pub fn distance(a: &TriadicCensus, b: &TriadicCensus) -> f64 {
        let mut sum = 0.0f64;
        for i in 0..16 {
            let va = a.census[i];
            let vb = b.census[i];
            let diff = (if va > vb {
                va - vb
            } else {
                vb - va
            }) as f64;
            sum += diff * diff;
        }
        sum.sqrt()
    }

    #[inline(always)]
    fn new() -> TriadicCensus {
        TriadicCensus { census: [0; 16] }
    }

    #[inline(always)]
    fn add(&mut self, triad_type: TriadType, cnt: u64) {
        self.census[triad_type as usize] += cnt;
    }

    #[inline(always)]
    fn set(&mut self, triad_type: TriadType, cnt: u64) {
        self.census[triad_type as usize] = cnt;
    }

    #[inline(always)]
    pub fn get(&self, triad_type: TriadType) -> u64 {
        self.census[triad_type as usize]
    }

    #[inline(always)]
    pub fn as_slice<'a>(&'a self) -> &'a [u64] {
        &self.census[..]
    }

    /// XXX: Optimize: find_edge() is O(k) with k the number of edges.
    pub fn from_graph<G: DirectedGraph>(graph: &G) -> TriadicCensus {
        let n = graph.node_count();

        let mut census = TriadicCensus::new();

        for v in 0..n {
            let mut neighbors_v = HashSet::new();
            graph.each_undirected_neighbor(v, |u| if u > v { neighbors_v.insert(u); });

            for u in neighbors_v {
                let mut s = HashSet::new(); // XXX: Use bitset? // reuse
                graph.each_undirected_neighbor(u, |nu| {s.insert(nu);});
                graph.each_undirected_neighbor(v, |nv| {s.insert(nv);});
                let _ = s.remove(&u);
                let _ = s.remove(&v);

                let tri_type = if graph.has_edge(v, u) && graph.has_edge(u, v) {
                    TriadType::T102
                } else {
                    TriadType::T012
                };

                let cnt = n as i64 - s.len() as i64 - 2;
                assert!(cnt >= 0);
                census.add(tri_type, cnt as u64);
                for w in s {
                    if u < w ||
                       (v < w && w < u &&
                        !(graph.has_edge(v, w) || graph.has_edge(w, v))) {
                        let tri_type = TriadType::from_u8(TRITYPES[tricode(graph, v, u, w)]);
                        census.add(tri_type, 1);
                    }
                }
            }
        }

        let mut sum = 0;
        for &cnt in &census.as_slice()[1..] {
            sum += cnt;
        }
        let n = n as u64;
        // Integer division below is guaranteed to produce an integral result
        census.set(TriadType::T003, (n * (n - 1) * (n - 2)) / 6 - sum);
        return census;
    }
}

pub struct SimpleDiGraph {
    g: Graph<(), (), Directed>
}

impl SimpleDiGraph {
    pub fn new() -> SimpleDiGraph {
        SimpleDiGraph {g: Graph::new()}
    }

    pub fn add_node(&mut self) -> NodeIdx {
        self.g.add_node(()).index()
    }

    pub fn add_edge(&mut self, src: NodeIdx, dst: NodeIdx) {
        self.g.add_edge(NodeIndex::new(src), NodeIndex::new(dst), ());
    }
}

impl DirectedGraph for SimpleDiGraph {
    fn node_count(&self) -> NodeIdx {
        self.g.node_count()
    }

    #[inline]
    fn has_edge(&self, src: NodeIdx, dst: NodeIdx) -> bool {
        self.g.find_edge(NodeIndex::new(src), NodeIndex::new(dst)).is_some()
    }

    #[inline]
    fn each_undirected_neighbor<F:FnMut(NodeIdx)>(&self, node: NodeIdx, mut callback: F) {
        for n in self.g.neighbors_undirected(NodeIndex::new(node)) {
            callback(n.index())
        }
    }
}

#[test]
fn test_simple() {
    let mut g = SimpleDiGraph::new();

    let n1 = g.add_node();
    let n2 = g.add_node();
    let n3 = g.add_node();

    let c1 = TriadicCensus::from_graph(&g);
    assert_eq!(&[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c1.as_slice());

    g.add_edge(n1, n3);
    let c2 = TriadicCensus::from_graph(&g);
    assert_eq!(&[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c2.as_slice());

    let d = TriadicCensus::distance(&c1, &c2); // should be sqrt(2)
    assert!(d > 1.414 && d < 1.415);

    g.add_edge(n3, n1);
    let c3 = TriadicCensus::from_graph(&g);
    assert_eq!(&[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c3.as_slice());

    g.add_edge(n2, n3);
    let c4 = TriadicCensus::from_graph(&g);
    assert_eq!(&[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c4.as_slice());
}

#[cfg(test)]
fn line_graph(n: u32) -> SimpleDiGraph {
    let mut g = SimpleDiGraph::new();

    let mut prev = g.add_node();
    for _ in 0..n - 1 {
        let cur = g.add_node();
        g.add_edge(prev, cur);
        prev = cur;
    }
    return g;
}

#[cfg(test)]
fn circular_graph(n: u32) -> SimpleDiGraph {
    let mut g = SimpleDiGraph::new();

    let first = g.add_node();
    let mut prev = first;
    for _ in 0..n - 1 {
        let cur = g.add_node();
        g.add_edge(prev, cur);
        prev = cur;
    }
    g.add_edge(prev, first);
    return g;
}

#[test]
fn test_line_graph() {
    // Result compared with igraph.triad_census().
    let c = TriadicCensus::from_graph(&line_graph(20));
    assert_eq!(&[816, 306, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c.as_slice());

    let c = TriadicCensus::from_graph(&line_graph(40));
    assert_eq!(&[8436, 1406, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c.as_slice());
}

#[test]
fn test_circular_graph() {
    // Result compared with igraph.triad_census().
    let c = TriadicCensus::from_graph(&circular_graph(20));
    assert_eq!(&[800, 320, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c.as_slice());

    let c = TriadicCensus::from_graph(&circular_graph(40));
    assert_eq!(&[8400, 1440, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               c.as_slice());

}
<|endoftext|>