<commit_before>use std::env;
use std::fs;
use std::io::{self, ErrorKind, Read, Write, BufWriter};
use std::process::exit;
use std::time::{Duration, Instant};

use getopts::Options;

use ppbert::bertterm::BertTerm;
use ppbert::consts::VERSION;
use ppbert::error::{BertError, Result};
use ppbert::parsers::*;

const PROG_NAME: &str = "ppbert";

fn opt_usize(m: &getopts::Matches, opt: &str, default: usize) -> usize {
    match m.opt_get_default(opt, default) {
        Ok(n) => n,
        Err(_) => {
            eprintln!("'{}' must be a number", opt);
            exit(1);
        }
    }
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("V", "version", "display version");
    opts.optflag("h", "help", "display this help");
    opts.optopt("i", "indent", "indent with NUM spaces", "NUM");
    opts.optopt("m", "per-line", "print at most NUM basic terms per line", "NUM");
    opts.optflag("p", "parse", "parse only, not pretty print");
    opts.optflag("2", "bert2", "parse .bert2 files");
    opts.optflag("d", "disk-log", "parse disk_log files");
    opts.optflag("v", "verbose", "show diagnostics on stderr");
    opts.optflag("j", "json", "print as JSON");
    opts.optflag("t", "transform-proplists", "convert proplists to JSON objects");

    let mut matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}: {}", PROG_NAME, e);
            eprintln!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
            exit(1);
        }
    };

    if matches.opt_present("help") {
        println!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, VERSION);
        exit(0);
    }

    // If no files to process, use stdin.
    if matches.free.is_empty() {
        matches.free.push("-".to_owned());
    }

    let indent_width = opt_usize(&matches, "indent", 2);
    let max_per_line = opt_usize(&matches, "per-line", 6);
    let parse_only = matches.opt_present("parse");
    let json = matches.opt_present("json");
    let transform_proplists = matches.opt_present("transform-proplists");
    let verbose = matches.opt_present("verbose");

    let mut parser: Box<dyn Parser> =
        if matches.opt_present("bert2") {
            Box::new(Bert2Parser::new())
        } else if matches.opt_present("disk-log") {
            Box::new(DiskLogParser::new())
        } else {
            Box::new(Bert1Parser::new())
        };
    let output_fn = match (json, transform_proplists) {
        (true, false)  => pp_json,
        (true, true)   => pp_json_proplist,
        (false, false) => pp_bert,
        (false, true)  => {
            eprintln!("{}: --transform-proplists is only valid with the --json flag", PROG_NAME);
            pp_bert
        }
    };

    let mut return_code = 0;
    for file in &matches.free {
        let res = handle_file(file, parse_only, verbose,
                              indent_width, max_per_line,
                              &mut parser, output_fn);
        match res {
            Ok(()) => (),
            Err(ref e) => {
                if broken_pipe(e) {
                    break;
                }
                return_code = 1;
                eprintln!("{}: {}: {}", PROG_NAME, file, e);
            }
        }
    }
    exit(return_code);
}

fn broken_pipe(err: &BertError) -> bool {
    match *err {
        BertError::IoError(ref ioerr) =>
            ioerr.kind() == ErrorKind::BrokenPipe,
        _ => false
    }
}

fn read_bytes(filename: &str) -> Result<Vec<u8>> {
    if filename == "-" {
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let mut buf: Vec<u8> = Vec::with_capacity(4096);
        stdin.read_to_end(&mut buf)?;
        return Ok(buf);
    } else {
        let buf = fs::read(filename)?;
        return Ok(buf);
    }
}

fn handle_file(
    filename: &str,
    parse_only: bool,
    verbose: bool,
    indent: usize,
    terms_per_line: usize,
    parser: &mut Box<dyn Parser>,
    pp_fn: fn(BertTerm, usize, usize) -> Result<()>
) -> Result<()> {

    // Read file or stdin into buffer
    let now = Instant::now();
    let buf = read_bytes(filename)?;
    let read_dur = now.elapsed();

    parser.set_input(buf);
    let mut parse_dur = Duration::new(0, 0);
    let mut pp_dur = Duration::new(0, 0);

    loop {
        let now = Instant::now();
        let next_item = parser.next();
        parse_dur += now.elapsed();

        match next_item {
            None => { break; }
            Some(Err(e)) => { return Err(e); }
            Some(Ok(t)) => {
                if !parse_only {
                    let now = Instant::now();
                    pp_fn(t, indent, terms_per_line)?;
                    pp_dur += now.elapsed();
                }
            }
        }
    }

    if verbose {
        eprintln!("{}: {} read time: {}.{:06} seconds", PROG_NAME, filename, read_dur.as_secs(), read_dur.subsec_micros());
        eprintln!("{}: {} parse time: {}.{:06} seconds", PROG_NAME, filename, parse_dur.as_secs(), parse_dur.subsec_micros());
        if !parse_only {
            eprintln!("{}: {} print time: {}.{:06} seconds", PROG_NAME, filename, pp_dur.as_secs(), pp_dur.subsec_micros());
        }
    }

    return Ok(());
}

/// Outputs a BertTerm to stdout.
fn pp_bert(term: BertTerm, indent_width: usize, terms_per_line: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_erlang(&mut stdout, indent_width, terms_per_line)?;
    stdout.flush()?;
    writeln!(&mut stdout, "")?;
    return Ok(());
}

/// Outputs a BertTerm as JSON to stdout.
fn pp_json(term: BertTerm, _: usize, _: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_json(&mut stdout, false)?;
    writeln!(&mut stdout, "")?;
    stdout.flush()?;
    return Ok(());
}

/// Outputs a BertTerm as JSON to stdout;
/// Erlang proplists are converted to JSON objects.
fn pp_json_proplist(term: BertTerm, _: usize, _: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_json(&mut stdout, true)?;
    writeln!(&mut stdout, "")?;
    stdout.flush()?;
    return Ok(());
}
<commit_msg>Implement file extension-based parser selection<commit_after>use std::env;
use std::fs;
use std::io::{self, ErrorKind, Read, Write, BufWriter};
use std::path::Path;
use std::process::exit;
use std::time::{Duration, Instant};

use getopts::Options;

use ppbert::bertterm::BertTerm;
use ppbert::consts::VERSION;
use ppbert::error::{BertError, Result};
use ppbert::parsers::*;

const PROG_NAME: &str = "ppbert";

#[derive(Clone, Copy)]
enum ParserChoice {
    ByExtension,
    ForceBert1,
    ForceBert2,
    ForceDiskLog,
}

fn opt_usize(m: &getopts::Matches, opt: &str, default: usize) -> usize {
    match m.opt_get_default(opt, default) {
        Ok(n) => n,
        Err(_) => {
            eprintln!("'{}' must be a number", opt);
            exit(1);
        }
    }
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("V", "version", "display version");
    opts.optflag("h", "help", "display this help");
    opts.optopt("i", "indent", "indent with NUM spaces", "NUM");
    opts.optopt("m", "per-line", "print at most NUM basic terms per line", "NUM");
    opts.optflag("p", "parse", "parse only, not pretty print");
    opts.optflag("1", "bert1", "force ppbert to use regular BERT parser");
    opts.optflag("2", "bert2", "force ppbert to use BERT2 parser");
    opts.optflag("d", "disk-log", "force ppbert to use DiskLog parser");
    opts.optflag("v", "verbose", "show diagnostics on stderr");
    opts.optflag("j", "json", "print as JSON");
    opts.optflag("t", "transform-proplists", "convert proplists to JSON objects");

    let mut matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}: {}", PROG_NAME, e);
            eprintln!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
            exit(1);
        }
    };

    if matches.opt_present("help") {
        println!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, VERSION);
        exit(0);
    }

    // If no files to process, use stdin.
    if matches.free.is_empty() {
        matches.free.push("-".to_owned());
    }

    let indent_width = opt_usize(&matches, "indent", 2);
    let max_per_line = opt_usize(&matches, "per-line", 6);
    let parse_only = matches.opt_present("parse");
    let json = matches.opt_present("json");
    let transform_proplists = matches.opt_present("transform-proplists");
    let verbose = matches.opt_present("verbose");

    let parser_choice =
        if matches.opt_present("bert1") {
            ParserChoice::ForceBert1
        } else if matches.opt_present("bert2") {
            ParserChoice::ForceBert2
        } else if matches.opt_present("disk-log") {
            ParserChoice::ForceDiskLog
        } else {
            ParserChoice::ByExtension
        };

    let output_fn = match (json, transform_proplists) {
        (true, false)  => pp_json,
        (true, true)   => pp_json_proplist,
        (false, false) => pp_bert,
        (false, true)  => {
            eprintln!("{}: --transform-proplists is only valid with the --json flag", PROG_NAME);
            pp_bert
        }
    };

    let mut return_code = 0;
    for file in &matches.free {
        let res = handle_file(file, parse_only, verbose,
                              indent_width, max_per_line,
                              parser_choice, output_fn);
        match res {
            Ok(()) => (),
            Err(ref e) => {
                if broken_pipe(e) {
                    break;
                }
                return_code = 1;
                eprintln!("{}: {}: {}", PROG_NAME, file, e);
            }
        }
    }
    exit(return_code);
}

fn broken_pipe(err: &BertError) -> bool {
    match *err {
        BertError::IoError(ref ioerr) =>
            ioerr.kind() == ErrorKind::BrokenPipe,
        _ => false
    }
}

fn read_bytes(filename: &str) -> Result<Vec<u8>> {
    if filename == "-" {
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let mut buf: Vec<u8> = Vec::with_capacity(4096);
        stdin.read_to_end(&mut buf)?;
        return Ok(buf);
    } else {
        let buf = fs::read(filename)?;
        return Ok(buf);
    }
}

fn parser_from_ext(filename: &str) -> Box<dyn Parser> {
    let ext: Option<&str> =
        Path::new(filename)
        .extension()
        .and_then(|x| x.to_str());
    match ext {
        Some("bert") | Some("bert1") => Box::new(Bert1Parser::new()),
        Some("bert2") => Box::new(Bert2Parser::new()),
        Some("log") => Box::new(DiskLogParser::new()),
        _ => {
            eprintln!("{}: cannot find an appropriate parser for {}; using BERT",
                      PROG_NAME, filename);
            Box::new(Bert1Parser::new())
        },
    }
}

fn handle_file(
    filename: &str,
    parse_only: bool,
    verbose: bool,
    indent: usize,
    terms_per_line: usize,
    parser_choice: ParserChoice,
    pp_fn: fn(BertTerm, usize, usize) -> Result<()>
) -> Result<()> {

    // Read file or stdin into buffer
    let now = Instant::now();
    let buf = read_bytes(filename)?;
    let read_dur = now.elapsed();

    let mut parser: Box<dyn Parser> = match parser_choice {
        ParserChoice::ForceBert1 => Box::new(Bert1Parser::new()),
        ParserChoice::ForceBert2 => Box::new(Bert2Parser::new()),
        ParserChoice::ForceDiskLog => Box::new(DiskLogParser::new()),
        ParserChoice::ByExtension => parser_from_ext(filename),
    };

    parser.set_input(buf);
    let mut parse_dur = Duration::new(0, 0);
    let mut pp_dur = Duration::new(0, 0);

    loop {
        let now = Instant::now();
        let next_item = parser.next();
        parse_dur += now.elapsed();

        match next_item {
            None => { break; }
            Some(Err(e)) => { return Err(e); }
            Some(Ok(t)) => {
                if !parse_only {
                    let now = Instant::now();
                    pp_fn(t, indent, terms_per_line)?;
                    pp_dur += now.elapsed();
                }
            }
        }
    }

    if verbose {
        eprintln!("{}: {} read time: {}.{:06} seconds", PROG_NAME, filename, read_dur.as_secs(), read_dur.subsec_micros());
        eprintln!("{}: {} parse time: {}.{:06} seconds", PROG_NAME, filename, parse_dur.as_secs(), parse_dur.subsec_micros());
        if !parse_only {
            eprintln!("{}: {} print time: {}.{:06} seconds", PROG_NAME, filename, pp_dur.as_secs(), pp_dur.subsec_micros());
        }
    }

    return Ok(());
}

/// Outputs a BertTerm to stdout.
fn pp_bert(term: BertTerm, indent_width: usize, terms_per_line: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_erlang(&mut stdout, indent_width, terms_per_line)?;
    stdout.flush()?;
    writeln!(&mut stdout, "")?;
    return Ok(());
}

/// Outputs a BertTerm as JSON to stdout.
fn pp_json(term: BertTerm, _: usize, _: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_json(&mut stdout, false)?;
    writeln!(&mut stdout, "")?;
    stdout.flush()?;
    return Ok(());
}

/// Outputs a BertTerm as JSON to stdout;
/// Erlang proplists are converted to JSON objects.
fn pp_json_proplist(term: BertTerm, _: usize, _: usize) -> Result<()> {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let mut stdout = BufWriter::new(stdout);
    term.write_as_json(&mut stdout, true)?;
    writeln!(&mut stdout, "")?;
    stdout.flush()?;
    return Ok(());
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
#![cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]

extern crate docopt;
extern crate env_logger;
extern crate hprof;
extern crate libgir as gir;

use std::cell::RefCell;
use std::str::FromStr;

use gir::{Config, WorkMode};

use gir::Library;
use hprof::Profiler;

static USAGE: &'static str = "
Usage: gir [options] [<library> <version>]
       gir --help

Options:
    -h, --help              Show this message.
    -c CONFIG               Config file path (default: Gir.toml)
    -d GIRSPATH             Directory for girs
    -m MODE                 Work mode: doc, normal, sys or not_bound
    -o PATH                 Target path
    --doc-target-path PATH  Doc target path
    -b, --make-backup       Make backup before generating
    -s, --stats             Show statistics
";

fn build_config() -> Result<Config, String> {
    let args = match docopt::Docopt::new(USAGE).and_then(|dopt| dopt.parse()) {
        Ok(args) => args,
        Err(e) => return Err(format!("Failed to get arguments: {}", e)),
    };
    let work_mode = match args.get_str("-m") {
        "" => None,
        s => match WorkMode::from_str(s) {
            Ok(w) => Some(w),
            Err(e) => {
                eprintln!("Error (switching to default work mode): {}", e);
                None
            }
        },
    };

    Config::new(args.get_str("-c"), work_mode, args.get_str("-d"),
                args.get_str("<library>"), args.get_str("<version>"), args.get_str("-o"),
                args.get_str("--doc-target-path"), args.get_bool("-b"), args.get_bool("-s"))
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    if let Err(ref e) = do_main() {
        eprintln!("{}", e);

        ::std::process::exit(1);
    }
}

fn do_main() -> Result<(), String> {
    if std::env::var_os("RUST_LOG").is_none() {
        std::env::set_var("RUST_LOG", "gir=warn");
    }
    env_logger::init();

    let mut cfg = match build_config() {
        Ok(cfg) => cfg,
        Err(err) => return Err(err),
    };

    let statistics = Profiler::new("Gir");
    statistics.start_frame();

    let watcher_total = statistics.enter("Total");

    let mut library;

    {
        let _watcher = statistics.enter("Loading");

        library = Library::new(&cfg.library_name);
        try!(library.read_file(&cfg.girs_dir, &cfg.library_full_name()));
    }

    {
        let _watcher = statistics.enter("Preprocessing");
        library.preprocessing(cfg.work_mode);
    }

    {
        let _watcher = statistics.enter("Update library by config");
        gir::update_version::apply_config(&mut library, &cfg);
    }

    {
        let _watcher = statistics.enter("Postprocessing");
        library.postprocessing();
    }

    {
        let _watcher = statistics.enter("Resolving type ids");
        cfg.resolve_type_ids(&library);
    }

    {
        let _watcher = statistics.enter("Checking versions");
        gir::update_version::check_function_real_version(&mut library);
    }

    let mut env;

    {
        let _watcher = statistics.enter("Namespace/symbol/class analysis");

        let namespaces = gir::namespaces_run(&library);
        let symbols = gir::symbols_run(&library, &namespaces);
        let class_hierarchy = gir::class_hierarchy_run(&library);

        env = gir::Env {
            library,
            config: cfg,
            namespaces,
            symbols: RefCell::new(symbols),
            class_hierarchy,
            analysis: Default::default(),
        };
    }

    if env.config.work_mode != WorkMode::Sys {
        let _watcher = statistics.enter("Analysing");
        gir::analysis_run(&mut env);
    }

    if env.config.work_mode != WorkMode::DisplayNotBound {
        let _watcher = statistics.enter("Generating");
        gir::codegen_generate(&env);
    }

    drop(watcher_total);
    statistics.end_frame();

    if env.config.show_statistics {
        statistics.print_timing();
    }
    if env.config.work_mode == WorkMode::DisplayNotBound {
        env.library.show_non_bound_types(&env);
    }

    Ok(())
}
<commit_msg>Enable logging from libgir component by default at warn level.<commit_after>#![cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
#![cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]

extern crate docopt;
extern crate env_logger;
extern crate hprof;
extern crate libgir as gir;

use std::cell::RefCell;
use std::str::FromStr;

use gir::{Config, WorkMode};

use gir::Library;
use hprof::Profiler;

static USAGE: &'static str = "
Usage: gir [options] [<library> <version>]
       gir --help

Options:
    -h, --help              Show this message.
    -c CONFIG               Config file path (default: Gir.toml)
    -d GIRSPATH             Directory for girs
    -m MODE                 Work mode: doc, normal, sys or not_bound
    -o PATH                 Target path
    --doc-target-path PATH  Doc target path
    -b, --make-backup       Make backup before generating
    -s, --stats             Show statistics
";

fn build_config() -> Result<Config, String> {
    let args = match docopt::Docopt::new(USAGE).and_then(|dopt| dopt.parse()) {
        Ok(args) => args,
        Err(e) => return Err(format!("Failed to get arguments: {}", e)),
    };
    let work_mode = match args.get_str("-m") {
        "" => None,
        s => match WorkMode::from_str(s) {
            Ok(w) => Some(w),
            Err(e) => {
                eprintln!("Error (switching to default work mode): {}", e);
                None
            }
        },
    };

    Config::new(args.get_str("-c"), work_mode, args.get_str("-d"),
                args.get_str("<library>"), args.get_str("<version>"), args.get_str("-o"),
                args.get_str("--doc-target-path"), args.get_bool("-b"), args.get_bool("-s"))
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    if let Err(ref e) = do_main() {
        eprintln!("{}", e);

        ::std::process::exit(1);
    }
}

fn do_main() -> Result<(), String> {
    if std::env::var_os("RUST_LOG").is_none() {
        std::env::set_var("RUST_LOG", "gir=warn");
        std::env::set_var("RUST_LOG", "libgir=warn");
    }
    env_logger::init();

    let mut cfg = match build_config() {
        Ok(cfg) => cfg,
        Err(err) => return Err(err),
    };

    let statistics = Profiler::new("Gir");
    statistics.start_frame();

    let watcher_total = statistics.enter("Total");

    let mut library;

    {
        let _watcher = statistics.enter("Loading");

        library = Library::new(&cfg.library_name);
        try!(library.read_file(&cfg.girs_dir, &cfg.library_full_name()));
    }

    {
        let _watcher = statistics.enter("Preprocessing");
        library.preprocessing(cfg.work_mode);
    }

    {
        let _watcher = statistics.enter("Update library by config");
        gir::update_version::apply_config(&mut library, &cfg);
    }

    {
        let _watcher = statistics.enter("Postprocessing");
        library.postprocessing();
    }

    {
        let _watcher = statistics.enter("Resolving type ids");
        cfg.resolve_type_ids(&library);
    }

    {
        let _watcher = statistics.enter("Checking versions");
        gir::update_version::check_function_real_version(&mut library);
    }

    let mut env;

    {
        let _watcher = statistics.enter("Namespace/symbol/class analysis");

        let namespaces = gir::namespaces_run(&library);
        let symbols = gir::symbols_run(&library, &namespaces);
        let class_hierarchy = gir::class_hierarchy_run(&library);

        env = gir::Env {
            library,
            config: cfg,
            namespaces,
            symbols: RefCell::new(symbols),
            class_hierarchy,
            analysis: Default::default(),
        };
    }

    if env.config.work_mode != WorkMode::Sys {
        let _watcher = statistics.enter("Analysing");
        gir::analysis_run(&mut env);
    }

    if env.config.work_mode != WorkMode::DisplayNotBound {
        let _watcher = statistics.enter("Generating");
        gir::codegen_generate(&env);
    }

    drop(watcher_total);
    statistics.end_frame();

    if env.config.show_statistics {
        statistics.print_timing();
    }
    if env.config.work_mode == WorkMode::DisplayNotBound {
        env.library.show_non_bound_types(&env);
    }

    Ok(())
}
<|endoftext|>


<commit_before>macro_rules! newtype_clone (($newtype:ident) => (
        impl Clone for $newtype {
            fn clone(&self) -> $newtype {
                let &$newtype(v) = self;
                $newtype(v)
            }
        }

        ));

macro_rules! newtype_drop (($newtype:ident) => (
        impl Drop for $newtype {
            fn drop(&mut self) {
                use libc::size_t;
                use ffi;
                let &mut $newtype(ref mut v) = self;
                unsafe {
                    ffi::sodium_memzero(v.as_mut_ptr(), v.len() as size_t);
                }
            }
        }
        ));

macro_rules! newtype_impl (($newtype:ident, $len:expr) => (
    impl $newtype {
        /// `from_slice()` creates an object from a byte slice
        ///
        /// This function will fail and return None if the length of
        /// the byte-slice isn't equal to the length of the object
        pub fn from_slice(bs: &[u8]) -> Option<$newtype> {
            if bs.len() != $len {
                return None;
            }
            let mut n = $newtype([0; $len]);
            {
                let $newtype(ref mut b) = n;
                for (bi, &bsi) in b.iter_mut().zip(bs.iter()) {
                    *bi = bsi
                }
            }
            Some(n)
        }
    }
    impl PartialEq for $newtype {
        fn eq(&self, &$newtype(ref other): &$newtype) -> bool {
            use crypto::verify::safe_memcmp;
            let &$newtype(ref this) = self;
            safe_memcmp(this, other)
        }
    }
    impl Eq for $newtype {}
    impl rustc_serialize::Encodable for $newtype {
        fn encode<E: rustc_serialize::Encoder>(&self, encoder: &mut E)
                -> Result<(), E::Error> {
            encoder.emit_seq($len, |encoder| {
                for (i, e) in self[..].iter().enumerate() {
                    try!(encoder.emit_seq_elt(i, |encoder| e.encode(encoder)))
                }
                Ok(())
            })
        }
    }
    impl rustc_serialize::Decodable for $newtype {
        fn decode<D: rustc_serialize::Decoder>(decoder: &mut D)
                -> Result<$newtype, D::Error> {
            decoder.read_seq(|decoder, len| {
                if len != $len {
                    return Err(decoder.error(
                        &format!("Expecting array of length: {}, but found {}",
                                 $len, len)));
                }
                let mut arr = [0u8; $len];
                for (i, val) in arr.iter_mut().enumerate() {
                    *val = try!(decoder.read_seq_elt(i,
                        |decoder| rustc_serialize::Decodable::decode(decoder)));
                }
                Ok($newtype(arr))
            })
        }
    }
    /// Allows a user to access the byte contents of an object as a slice.
    ///
    /// WARNING: it might be tempting to do comparisons on objects
    /// by using `x[a..b] == y[a..b]`. This will open up for timing attacks
    /// when comparing for example authenticator tags. Because of this only
    /// use the comparison functions exposed by the sodiumoxide API.
    impl ::std::ops::Index<::std::ops::Range<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: ::std::ops::Range<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /// Allows a user to access the byte contents of an object as a slice.
    ///
    /// WARNING: it might be tempting to do comparisons on objects
    /// by using `x[..b] == y[..b]`. This will open up for timing attacks
    /// when comparing for example authenticator tags. Because of this only
    /// use the comparison functions exposed by the sodiumoxide API.
    impl ::std::ops::Index<::std::ops::RangeTo<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: ::std::ops::RangeTo<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /// Allows a user to access the byte contents of an object as a slice.
    ///
    /// WARNING: it might be tempting to do comparisons on objects
    /// by using `x[a..] == y[a..]`. This will open up for timing attacks
    /// when comparing for example authenticator tags. Because of this only
    /// use the comparison functions exposed by the sodiumoxide API.
    impl ::std::ops::Index<::std::ops::RangeFrom<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: ::std::ops::RangeFrom<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /// Allows a user to access the byte contents of an object as a slice.
    ///
    /// WARNING: it might be tempting to do comparisons on objects
    /// by using `x[] == y[]`. This will open up for timing attacks
    /// when comparing for example authenticator tags. Because of this only
    /// use the comparison functions exposed by the sodiumoxide API.
    impl ::std::ops::Index<::std::ops::RangeFull> for $newtype {
        type Output = [u8];
        fn index(&self, _index: ::std::ops::RangeFull) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    ));
<commit_msg>Added Debug trait to newtype_impl macro.<commit_after>macro_rules! newtype_clone (($newtype:ident) => (
        impl Clone for $newtype {
            fn clone(&self) -> $newtype {
                let &$newtype(v) = self;
                $newtype(v)
            }
        }

        ));

macro_rules! newtype_drop (($newtype:ident) => (
        impl Drop for $newtype {
            fn drop(&mut self) {
                use libc::size_t;
                use ffi;
                let &mut $newtype(ref mut v) = self;
                unsafe {
                    ffi::sodium_memzero(v.as_mut_ptr(), v.len() as size_t);
                }
            }
        }
        ));

macro_rules! newtype_impl (($newtype:ident, $len:expr) => (
    impl $newtype {
        /// `from_slice()` creates an object from a byte slice
        ///
        /// This function will fail and return None if the length of
        /// the byte-slice isn't equal to the length of the object
        pub fn from_slice(bs: &[u8]) -> Option<$newtype> {
            if bs.len() != $len {
                return None;
            }
            let mut n = $newtype([0; $len]);
            {
                let $newtype(ref mut b) = n;
                for (bi, &bsi) in b.iter_mut().zip(bs.iter()) {
                    *bi = bsi
                }
            }
            Some(n)
        }
    }
    impl PartialEq for $newtype {
        fn eq(&self, &$newtype(ref other): &$newtype) -> bool {
            use crypto::verify::safe_memcmp;
            let &$newtype(ref this) = self;
            safe_memcmp(this, other)
        }
    }
    impl Eq for $newtype {}
    impl rustc_serialize::Encodable for $newtype {
        fn encode<E: rustc_serialize::Encoder>(&self, encoder: &mut E)
                -> Result<(), E::Error> {
            encoder.emit_seq($len, |encoder| {
                for (i, e) in self[..].iter().enumerate() {
                    try!(encoder.emit_seq_elt(i, |encoder| e.encode(encoder)))
                }
                Ok(())
            })
        }
    }
    impl rustc_serialize::Decodable for $newtype {
        fn decode<D: rustc_serialize::Decoder>(decoder: &mut D)
                -> Result<$newtype, D::Error> {
            decoder.read_seq(|decoder, len| {
                if len != $len {
                    return Err(decoder.error(
                        &format!("Expecting array of length: {}, but found {}",
                                 $len, len)));
                }
                let mut arr = [0u8; $len];
                for (i, val) in arr.iter_mut().enumerate() {
                    *val = try!(decoder.read_seq_elt(i,
                        |decoder| rustc_serialize::Decodable::decode(decoder)));
                }
                Ok($newtype(arr))
            })
        }
    }
    /// Allows a user to access the byte contents of an object as a slice.
    ///
    /// WARNING: it might be tempting to do comparisons on objects
    /// by using `x[a..b] == y[a..b]`. This will open up for timing attacks
    /// when comparing for example authenticator tags. Because of this only
    /// use the comparison functions exposed by the sodiumoxide API.
    impl ::std::ops::Index<::std::ops::Range<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: ::std::ops::Range<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /// Allows a user to access the byte contents of an object as a slice.
    ///
    /// WARNING: it might be tempting to do comparisons on objects
    /// by using `x[..b] == y[..b]`. This will open up for timing attacks
    /// when comparing for example authenticator tags. Because of this only
    /// use the comparison functions exposed by the sodiumoxide API.
    impl ::std::ops::Index<::std::ops::RangeTo<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: ::std::ops::RangeTo<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /// Allows a user to access the byte contents of an object as a slice.
    ///
    /// WARNING: it might be tempting to do comparisons on objects
    /// by using `x[a..] == y[a..]`. This will open up for timing attacks
    /// when comparing for example authenticator tags. Because of this only
    /// use the comparison functions exposed by the sodiumoxide API.
    impl ::std::ops::Index<::std::ops::RangeFrom<usize>> for $newtype {
        type Output = [u8];
        fn index(&self, _index: ::std::ops::RangeFrom<usize>) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    /// Allows a user to access the byte contents of an object as a slice.
    ///
    /// WARNING: it might be tempting to do comparisons on objects
    /// by using `x[] == y[]`. This will open up for timing attacks
    /// when comparing for example authenticator tags. Because of this only
    /// use the comparison functions exposed by the sodiumoxide API.
    impl ::std::ops::Index<::std::ops::RangeFull> for $newtype {
        type Output = [u8];
        fn index(&self, _index: ::std::ops::RangeFull) -> &[u8] {
            let &$newtype(ref b) = self;
            b.index(_index)
        }
    }
    impl ::std::fmt::Debug for $newtype  {
        fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
            write!(formatter, "{}({:?})", stringify!($newtype), &self[..])
        }
    }
    ));
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::Arc;

use rustfft::FFTplanner;
use common;
use ::{DCT1, DST1, TransformType4, TransformType2And3};
use mdct::*;
use algorithm::*;
use algorithm::type2and3_butterflies::*;

const DCT2_BUTTERFLIES: [usize; 4] = [2, 3, 4, 8, 16];

/// The DCT planner is used to make new DCT algorithm instances.
///
/// RustDCT has several DCT algorithms available for each DCT type; For a given DCT type and problem size, the FFTplanner
/// decides which of the available DCT algorithms to use and then initializes them.
///
/// ~~~
/// // Perform a DCT Type 4 of size 1234
/// use std::sync::Arc;
/// use rustdct::DCTplanner;
///
/// let mut input:  Vec<f32> = vec![0f32; 1234];
/// let mut output: Vec<f32> = vec![0f32; 1234];
///
/// let mut planner = DCTplanner::new();
/// let dct4 = planner.plan_dct4(1234);
/// dct4.process_dct4(&mut input, &mut output);
///
/// // The DCT instance returned by the planner is stored behind an `Arc`, so it's cheap to clone
/// let dct4_clone = Arc::clone(&dct4);
/// ~~~
///
/// If you plan on creating multiple DCT instances, it is recommnded to reuse the same planner for all of them. This
/// is because the planner re-uses internal data across DCT instances wherever possible, saving memory and reducing
/// setup time. (DCT instances created with one planner will never re-use data and buffers with DCT instances created
/// by a different planner)
///
/// Each DCT instance owns `Arc`s to its shared internal data, rather than borrowing it from the planner, so it's
/// perfectly safe to drop the planner after creating DCT instances.
pub struct DCTplanner<T> {
    fft_planner: FFTplanner<T>,
    dct1_cache: HashMap<usize, Arc<DCT1<T>>>,
    dst1_cache: HashMap<usize, Arc<DST1<T>>>,
    dct23_cache: HashMap<usize, Arc<TransformType2And3<T>>>,
    dct4_cache: HashMap<usize, Arc<TransformType4<T>>>,
    mdct_cache: HashMap<usize, Arc<MDCT<T>>>,
}
impl<T: common::DCTnum> DCTplanner<T> {
    pub fn new() -> Self {
        Self {
            fft_planner: FFTplanner::new(false),
            dct1_cache: HashMap::new(),
            dst1_cache: HashMap::new(),
            dct23_cache: HashMap::new(),
            dct4_cache: HashMap::new(),
            mdct_cache: HashMap::new(),
        }
    }

    /// Returns a DCT Type 1 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dct1(&mut self, len: usize) -> Arc<DCT1<T>> {
        if self.dct1_cache.contains_key(&len) {
            Arc::clone(self.dct1_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_dct1(len);
            self.dct1_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_dct1(&mut self, len: usize) -> Arc<DCT1<T>> {
        //benchmarking shows that below about 25, it's faster to just use the naive DCT1 algorithm
        if len < 25 {
            Arc::new(DCT1Naive::new(len))
        } else {
            let fft = self.fft_planner.plan_fft((len - 1) * 2);
            Arc::new(DCT1ConvertToFFT::new(fft))
        }
    }

    /// Returns a DCT Type 2 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dct2(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        if self.dct23_cache.contains_key(&len) {
            Arc::clone(self.dct23_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_dct2(len);
            self.dct23_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_dct2(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        if DCT2_BUTTERFLIES.contains(&len) {
            self.plan_dct2_butterfly(len)
        } else if len.is_power_of_two() && len > 2 {
            let half_dct = self.plan_dct2(len / 2);
            let quarter_dct = self.plan_dct2(len / 4);
            Arc::new(Type2And3SplitRadix::new(half_dct, quarter_dct))
        } else if len < 16 {
            //benchmarking shows that below about 16, it's faster to just use the naive DCT2 algorithm
            Arc::new(Type2And3Naive::new(len))
        } else {
            let fft = self.fft_planner.plan_fft(len);
            Arc::new(Type2And3ConvertToFFT::new(fft))
        }
    }

    fn plan_dct2_butterfly(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        match len {
            2 => Arc::new(Type2And3Butterfly2::new()),
            3 => Arc::new(Type2And3Butterfly3::new()),
            4 => Arc::new(Type2And3Butterfly4::new()),
            8 => Arc::new(Type2And3Butterfly8::new()),
            16 => Arc::new(Type2And3Butterfly16::new()),
            _ => panic!("Invalid butterfly size for DCT2: {}", len)
        }
    }

    /// Returns DCT Type 3 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dct3(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        self.plan_dct2(len)
    }

    /// Returns a DCT Type 4 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dct4(&mut self, len: usize) -> Arc<TransformType4<T>> {
        if self.dct4_cache.contains_key(&len) {
            Arc::clone(self.dct4_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_dct4(len);
            self.dct4_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_dct4(&mut self, len: usize) -> Arc<TransformType4<T>> {
        //if we have an even size, we can use the DCT4 Via DCT3 algorithm
        if len % 2 == 0 {
            //benchmarking shows that below 6, it's faster to just use the naive DCT4 algorithm
            if len < 6 {
                Arc::new(Type4Naive::new(len))
            } else {
                let inner_dct = self.plan_dct3(len / 2);
                Arc::new(Type4ConvertToType3Even::new(inner_dct))
            }
        } else {
            //odd size, so we can use the "DCT4 via FFT odd" algorithm
            //benchmarking shows that below about 7, it's faster to just use the naive DCT4 algorithm
            if len < 7 {
                Arc::new(Type4Naive::new(len))
            } else {
                let fft = self.fft_planner.plan_fft(len);
                Arc::new(Type4ConvertToFFTOdd::new(fft))
            }
        }
    }

    /// Returns a DST Type 1 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dst1(&mut self, len: usize) -> Arc<DST1<T>> {
        if self.dst1_cache.contains_key(&len) {
            Arc::clone(self.dst1_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_dst1(len);
            self.dst1_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_dst1(&mut self, len: usize) -> Arc<DST1<T>> {
        //benchmarking shows that below about 25, it's faster to just use the naive DCT1 algorithm
        if len < 25 {
            Arc::new(DST1Naive::new(len))
        } else {
            let fft = self.fft_planner.plan_fft((len + 1) * 2);
            Arc::new(DST1ConvertToFFT::new(fft))
        }
    }

    /// Returns DST Type 2 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dst2(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        self.plan_dct2(len)
    }

    /// Returns DST Type 3 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dst3(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        self.plan_dct2(len)
    }

    /// Returns DST Type 4 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dst4(&mut self, len: usize) -> Arc<TransformType4<T>> {
        self.plan_dct4(len)
    }

    /// Returns a MDCT instance which processes inputs of size ` len * 2` and produces outputs of size `len`.
    ///
    /// `window_fn` is a function that takes a `size` and returns a `Vec` containing `size` window values.
    /// See the [`window_fn`](mdct/window_fn/index.html) module for provided window functions.
    ///
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_mdct<F>(&mut self, len: usize, window_fn: F) -> Arc<MDCT<T>>
    where F: (FnOnce(usize) -> Vec<T>) {
        if self.mdct_cache.contains_key(&len) {
            Arc::clone(self.mdct_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_mdct(len, window_fn);
            self.mdct_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_mdct<F>(&mut self, len: usize, window_fn: F) -> Arc<MDCT<T>>
    where F: (FnOnce(usize) -> Vec<T>) {
        //benchmarking shows that using the inner dct4 algorithm is always faster than computing the naive algorithm
        let inner_dct4 = self.plan_dct4(len);
        Arc::new(MDCTViaDCT4::new(inner_dct4, window_fn))
    }
}
<commit_msg>Forgot to update array type<commit_after>use std::collections::HashMap;
use std::sync::Arc;

use rustfft::FFTplanner;
use common;
use ::{DCT1, DST1, TransformType4, TransformType2And3};
use mdct::*;
use algorithm::*;
use algorithm::type2and3_butterflies::*;

const DCT2_BUTTERFLIES: [usize; 5] = [2, 3, 4, 8, 16];

/// The DCT planner is used to make new DCT algorithm instances.
///
/// RustDCT has several DCT algorithms available for each DCT type; For a given DCT type and problem size, the FFTplanner
/// decides which of the available DCT algorithms to use and then initializes them.
///
/// ~~~
/// // Perform a DCT Type 4 of size 1234
/// use std::sync::Arc;
/// use rustdct::DCTplanner;
///
/// let mut input:  Vec<f32> = vec![0f32; 1234];
/// let mut output: Vec<f32> = vec![0f32; 1234];
///
/// let mut planner = DCTplanner::new();
/// let dct4 = planner.plan_dct4(1234);
/// dct4.process_dct4(&mut input, &mut output);
///
/// // The DCT instance returned by the planner is stored behind an `Arc`, so it's cheap to clone
/// let dct4_clone = Arc::clone(&dct4);
/// ~~~
///
/// If you plan on creating multiple DCT instances, it is recommnded to reuse the same planner for all of them. This
/// is because the planner re-uses internal data across DCT instances wherever possible, saving memory and reducing
/// setup time. (DCT instances created with one planner will never re-use data and buffers with DCT instances created
/// by a different planner)
///
/// Each DCT instance owns `Arc`s to its shared internal data, rather than borrowing it from the planner, so it's
/// perfectly safe to drop the planner after creating DCT instances.
pub struct DCTplanner<T> {
    fft_planner: FFTplanner<T>,
    dct1_cache: HashMap<usize, Arc<DCT1<T>>>,
    dst1_cache: HashMap<usize, Arc<DST1<T>>>,
    dct23_cache: HashMap<usize, Arc<TransformType2And3<T>>>,
    dct4_cache: HashMap<usize, Arc<TransformType4<T>>>,
    mdct_cache: HashMap<usize, Arc<MDCT<T>>>,
}
impl<T: common::DCTnum> DCTplanner<T> {
    pub fn new() -> Self {
        Self {
            fft_planner: FFTplanner::new(false),
            dct1_cache: HashMap::new(),
            dst1_cache: HashMap::new(),
            dct23_cache: HashMap::new(),
            dct4_cache: HashMap::new(),
            mdct_cache: HashMap::new(),
        }
    }

    /// Returns a DCT Type 1 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dct1(&mut self, len: usize) -> Arc<DCT1<T>> {
        if self.dct1_cache.contains_key(&len) {
            Arc::clone(self.dct1_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_dct1(len);
            self.dct1_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_dct1(&mut self, len: usize) -> Arc<DCT1<T>> {
        //benchmarking shows that below about 25, it's faster to just use the naive DCT1 algorithm
        if len < 25 {
            Arc::new(DCT1Naive::new(len))
        } else {
            let fft = self.fft_planner.plan_fft((len - 1) * 2);
            Arc::new(DCT1ConvertToFFT::new(fft))
        }
    }

    /// Returns a DCT Type 2 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dct2(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        if self.dct23_cache.contains_key(&len) {
            Arc::clone(self.dct23_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_dct2(len);
            self.dct23_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_dct2(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        if DCT2_BUTTERFLIES.contains(&len) {
            self.plan_dct2_butterfly(len)
        } else if len.is_power_of_two() && len > 2 {
            let half_dct = self.plan_dct2(len / 2);
            let quarter_dct = self.plan_dct2(len / 4);
            Arc::new(Type2And3SplitRadix::new(half_dct, quarter_dct))
        } else if len < 16 {
            //benchmarking shows that below about 16, it's faster to just use the naive DCT2 algorithm
            Arc::new(Type2And3Naive::new(len))
        } else {
            let fft = self.fft_planner.plan_fft(len);
            Arc::new(Type2And3ConvertToFFT::new(fft))
        }
    }

    fn plan_dct2_butterfly(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        match len {
            2 => Arc::new(Type2And3Butterfly2::new()),
            3 => Arc::new(Type2And3Butterfly3::new()),
            4 => Arc::new(Type2And3Butterfly4::new()),
            8 => Arc::new(Type2And3Butterfly8::new()),
            16 => Arc::new(Type2And3Butterfly16::new()),
            _ => panic!("Invalid butterfly size for DCT2: {}", len)
        }
    }

    /// Returns DCT Type 3 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dct3(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        self.plan_dct2(len)
    }

    /// Returns a DCT Type 4 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dct4(&mut self, len: usize) -> Arc<TransformType4<T>> {
        if self.dct4_cache.contains_key(&len) {
            Arc::clone(self.dct4_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_dct4(len);
            self.dct4_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_dct4(&mut self, len: usize) -> Arc<TransformType4<T>> {
        //if we have an even size, we can use the DCT4 Via DCT3 algorithm
        if len % 2 == 0 {
            //benchmarking shows that below 6, it's faster to just use the naive DCT4 algorithm
            if len < 6 {
                Arc::new(Type4Naive::new(len))
            } else {
                let inner_dct = self.plan_dct3(len / 2);
                Arc::new(Type4ConvertToType3Even::new(inner_dct))
            }
        } else {
            //odd size, so we can use the "DCT4 via FFT odd" algorithm
            //benchmarking shows that below about 7, it's faster to just use the naive DCT4 algorithm
            if len < 7 {
                Arc::new(Type4Naive::new(len))
            } else {
                let fft = self.fft_planner.plan_fft(len);
                Arc::new(Type4ConvertToFFTOdd::new(fft))
            }
        }
    }

    /// Returns a DST Type 1 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dst1(&mut self, len: usize) -> Arc<DST1<T>> {
        if self.dst1_cache.contains_key(&len) {
            Arc::clone(self.dst1_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_dst1(len);
            self.dst1_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_dst1(&mut self, len: usize) -> Arc<DST1<T>> {
        //benchmarking shows that below about 25, it's faster to just use the naive DCT1 algorithm
        if len < 25 {
            Arc::new(DST1Naive::new(len))
        } else {
            let fft = self.fft_planner.plan_fft((len + 1) * 2);
            Arc::new(DST1ConvertToFFT::new(fft))
        }
    }

    /// Returns DST Type 2 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dst2(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        self.plan_dct2(len)
    }

    /// Returns DST Type 3 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dst3(&mut self, len: usize) -> Arc<TransformType2And3<T>> {
        self.plan_dct2(len)
    }

    /// Returns DST Type 4 instance which processes signals of size `len`.
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_dst4(&mut self, len: usize) -> Arc<TransformType4<T>> {
        self.plan_dct4(len)
    }

    /// Returns a MDCT instance which processes inputs of size ` len * 2` and produces outputs of size `len`.
    ///
    /// `window_fn` is a function that takes a `size` and returns a `Vec` containing `size` window values.
    /// See the [`window_fn`](mdct/window_fn/index.html) module for provided window functions.
    ///
    /// If this is called multiple times, it will attempt to re-use internal data between instances
    pub fn plan_mdct<F>(&mut self, len: usize, window_fn: F) -> Arc<MDCT<T>>
    where F: (FnOnce(usize) -> Vec<T>) {
        if self.mdct_cache.contains_key(&len) {
            Arc::clone(self.mdct_cache.get(&len).unwrap())
        } else {
            let result = self.plan_new_mdct(len, window_fn);
            self.mdct_cache.insert(len, Arc::clone(&result));
            result
        }
    }

    fn plan_new_mdct<F>(&mut self, len: usize, window_fn: F) -> Arc<MDCT<T>>
    where F: (FnOnce(usize) -> Vec<T>) {
        //benchmarking shows that using the inner dct4 algorithm is always faster than computing the naive algorithm
        let inner_dct4 = self.plan_dct4(len);
        Arc::new(MDCTViaDCT4::new(inner_dct4, window_fn))
    }
}
<|endoftext|>


<commit_before>//! POET - the Performance with Optimal Energy Toolkit. Used to meet timing constraints while
//! minimizing energy consumption.

use libc::{self, c_void, c_char, c_int, c_uint, c_double};
use std::ffi::CString;
use std::ptr;
use std::mem;

/// Typedef for an "apply" function - used to manipulate system or application settings.
pub type POETApplyFn = unsafe extern fn(states: *mut c_void,
                                        num_states: c_uint,
                                        id: c_uint,
                                        last_id: c_uint);

/// Typedef for "current state" function - used to try and determine the current system or
/// application state.
pub type POETCurrentStateFn = unsafe extern fn(states: *const c_void,
                                               num_states: c_uint,
                                               curr_state_id: *mut c_uint) -> i32;

#[link(name = "poet")]
extern {
    /*
     * Core functions - poet.h
     */

    fn poet_init(perf_goal: c_double,
                 num_system_states: c_uint,
                 control_states: *mut POETControlState,
                 apply_states: *mut POETCpuState,
                 apply_func: POETApplyFn,
                 curr_state_func: POETCurrentStateFn,
                 period: c_uint,
                 buffer_depth: c_uint,
                 log_filename: *const c_char) -> *mut c_void;

    fn poet_apply_control(state: *mut c_void,
                          id: u64,
                          perf: c_double,
                          pwr: c_double);

    fn poet_destroy(state: *mut c_void);

    /*
     * Configuration/utility functions - poet_config.h
     */

    fn apply_cpu_config(states: *mut c_void,
                        num_states: c_uint,
                        id: c_uint,
                        last_id: c_uint);

    fn get_control_states(path: *const c_char,
                          states: *mut *mut POETControlState,
                          num_states: *mut c_uint) -> c_int;

    fn get_cpu_states(path: *const c_char,
                      states: *mut *mut POETCpuState,
                      num_states: *mut c_uint) -> c_int;

    fn get_current_cpu_state(states: *const c_void,
                             num_states: c_uint,
                             curr_state_id: *mut c_uint) -> c_int;

}

fn add_null_terminator(f: &str) -> CString {
    let mut ntf: String = f.to_string();
    ntf.push('\0');
    CString::new(&ntf[..]).unwrap()
}

#[repr(C)]
#[derive(Copy, Clone)]
/// Representation of native struct `poet_control_state_t`.
pub struct POETControlState {
    pub id: c_uint,
    pub speedup: c_double,
    pub cost: c_double,
}

impl POETControlState {
    /// Attempt to load control states from a file.
    pub fn new(filename: Option<&str>) -> Result<Vec<POETControlState>, &'static str> {
        let filename = match filename {
            Some(f) => add_null_terminator(f).as_ptr(),
            None => ptr::null(),
        };
        let mut states: *mut POETControlState = ptr::null_mut::<POETControlState>();
        unsafe {
            let mut nstates: u32 = 0;
            let res = get_control_states(filename,
                                         &mut states,
                                         &mut nstates);
            if res != 0 {
                return Err("Failed to load control states");
            }
            let csvec: Vec<POETControlState> = Vec::from_raw_parts(states, nstates as usize, nstates as usize);
            // clone so we can free C-allocated memory (so user doesn't have to)
            let ret = csvec.clone();
            mem::forget(csvec);
            libc::free(states as *mut c_void);
            Ok(ret)
        }
    }
}

impl Default for POETControlState {
    fn default() -> POETControlState {
        POETControlState {
            id: 0,
            speedup: 1.0,
            cost: 1.0,
        }
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
/// Representation of native struct `poet_cpu_state_t`.
pub struct POETCpuState {
    id: c_uint,
    freq: c_uint,
    cores: c_uint,
}

impl POETCpuState {
    /// Attempt to load cpu states from a file.
    pub fn new(filename: Option<&str>) -> Result<Vec<POETCpuState>, &'static str> {
        let filename = match filename {
            Some(f) => add_null_terminator(f).as_ptr(),
            None => ptr::null(),
        };
        let mut states: *mut POETCpuState = ptr::null_mut::<POETCpuState>();
        unsafe {
            let mut nstates: u32 = 0;
            let res = get_cpu_states(filename,
                                     &mut states,
                                     &mut nstates);
            if res != 0 {
                return Err("Failed to load cpu states");
            }
            let csvec: Vec<POETCpuState> = Vec::from_raw_parts(states, nstates as usize, nstates as usize);
            // clone so we can free C-allocated memory (so user doesn't have to)
            let ret = csvec.clone();
            mem::forget(csvec);
            libc::free(states as *mut c_void);
            Ok(ret)
        }
    }
}

impl Default for POETCpuState {
    fn default() -> POETCpuState {
        POETCpuState {
            id: 0,
            freq: 0,
            cores: 0,
        }
    }
}

/// The `POET` struct wraps an underyling C struct.
pub struct POET {
    /// The underlying C struct `poet_state`.
    pub poet: *mut c_void,
}

impl POET {
    /// Attempt to initialize POET and allocate resources in the underlying C struct.
    pub fn new(perf_goal: f64,
               control_states: &mut Vec<POETControlState>,
               cpu_states: &mut Vec<POETCpuState>,
               apply_func: Option<POETApplyFn>,
               curr_state_func: Option<POETCurrentStateFn>,
               period: u32,
               buffer_depth: u32,
               log_filename: Option<&str>) -> Result<POET, &'static str> {
        if control_states.len() != cpu_states.len() {
            return Err("Number of control and cpu states don't match");
        }
        let apply_func = match apply_func {
            Some(p) => p,
            None => apply_cpu_config,
        };
        let curr_state_func = match curr_state_func {
            Some(p) => p,
            None => get_current_cpu_state,
        };
        let log_filename = match log_filename {
            Some(l) => add_null_terminator(l).as_ptr(),
            None => ptr::null(),
        };
        let poet = unsafe {
            let num_states = control_states.len() as u32;
            poet_init(perf_goal,
                      num_states, control_states.as_mut_ptr(), cpu_states.as_mut_ptr(),
                      apply_func, curr_state_func,
                      period, buffer_depth, log_filename)
        };
        if poet.is_null() {
            return Err("Failed to instantiate POET object");
        }
        Ok(POET { poet: poet, })
    }

    /// Call at every iteration - at specified periods this function will (potentially) order
    /// changes to system or application state to try and meet timing constraints.
    pub fn apply_control(&mut self, tag: u64, window_rate: f64, window_power: f64) {
        unsafe {
            poet_apply_control(self.poet, tag, window_rate, window_power);
        }
    }
}

impl Drop for POET {
    /// Cleanup POET and deallocate resources in the underlying C struct.
    fn drop(&mut self) {
        unsafe {
            poet_destroy(self.poet);
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use libc::{c_void, c_uint};

    #[test]
    fn test_basic() {
        let mut control_states = vec![POETControlState::default()];
        let mut cpu_states = vec![POETCpuState::default()];
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 None, None,
                                 20u32, 1u32, None).unwrap();
        poet.apply_control(0, 1.0, 1.0);
    }

    #[test]
    fn test_control_cpu_files_with_log() {
        let mut control_states = POETControlState::new(Some("test/control_config")).unwrap();
        let mut cpu_states = POETCpuState::new(Some("test/cpu_config")).unwrap();
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 None, None,
                                 20u32, 1u32, Some("poet.log")).unwrap();
        poet.apply_control(0, 1.0, 1.0);
    }

    #[test]
    fn test_rust_callbacks() {
        let mut control_states = vec![POETControlState::default()];
        let mut cpu_states = vec![POETCpuState::default()];
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 Some(dummy_apply), Some(dummy_curr_state),
                                 20u32, 1u32, None).unwrap();
        for i in 0..50 {
            poet.apply_control(i, 1.0, 1.0);
        }
    }

    unsafe extern fn dummy_apply(_states: *mut c_void,
                                 _num_states: c_uint,
                                 _id: c_uint,
                                 _last_id: c_uint) {
        // do nothing
        println!("Received apply call");
    }

    unsafe extern fn dummy_curr_state (_states: *const c_void,
                                       _num_states: c_uint,
                                       _curr_state_id: *mut c_uint) -> i32 {
        println!("Received curr state call");
        // this is actually an invalid value, but forces the apply function to be called
        *_curr_state_id = _num_states;
        0
    }

}
<commit_msg>Use CString ref as filename parameters<commit_after>//! POET - the Performance with Optimal Energy Toolkit. Used to meet timing constraints while
//! minimizing energy consumption.

use libc::{self, c_void, c_char, c_int, c_uint, c_double};
use std::ffi::CString;
use std::ptr;
use std::mem;

/// Typedef for an "apply" function - used to manipulate system or application settings.
pub type POETApplyFn = unsafe extern fn(states: *mut c_void,
                                        num_states: c_uint,
                                        id: c_uint,
                                        last_id: c_uint);

/// Typedef for "current state" function - used to try and determine the current system or
/// application state.
pub type POETCurrentStateFn = unsafe extern fn(states: *const c_void,
                                               num_states: c_uint,
                                               curr_state_id: *mut c_uint) -> i32;

#[link(name = "poet")]
extern {
    /*
     * Core functions - poet.h
     */

    fn poet_init(perf_goal: c_double,
                 num_system_states: c_uint,
                 control_states: *mut POETControlState,
                 apply_states: *mut POETCpuState,
                 apply_func: POETApplyFn,
                 curr_state_func: POETCurrentStateFn,
                 period: c_uint,
                 buffer_depth: c_uint,
                 log_filename: *const c_char) -> *mut c_void;

    fn poet_apply_control(state: *mut c_void,
                          id: u64,
                          perf: c_double,
                          pwr: c_double);

    fn poet_destroy(state: *mut c_void);

    /*
     * Configuration/utility functions - poet_config.h
     */

    fn apply_cpu_config(states: *mut c_void,
                        num_states: c_uint,
                        id: c_uint,
                        last_id: c_uint);

    fn get_control_states(path: *const c_char,
                          states: *mut *mut POETControlState,
                          num_states: *mut c_uint) -> c_int;

    fn get_cpu_states(path: *const c_char,
                      states: *mut *mut POETCpuState,
                      num_states: *mut c_uint) -> c_int;

    fn get_current_cpu_state(states: *const c_void,
                             num_states: c_uint,
                             curr_state_id: *mut c_uint) -> c_int;

}

#[repr(C)]
#[derive(Copy, Clone)]
/// Representation of native struct `poet_control_state_t`.
pub struct POETControlState {
    pub id: c_uint,
    pub speedup: c_double,
    pub cost: c_double,
}

impl POETControlState {
    /// Attempt to load control states from a file.
    pub fn new(filename: Option<&CString>) -> Result<Vec<POETControlState>, &'static str> {
        let name_ptr = match filename {
            Some(f) => f.as_ptr(),
            None => ptr::null(),
        };
        let mut states: *mut POETControlState = ptr::null_mut::<POETControlState>();
        unsafe {
            let mut nstates: u32 = 0;
            let res = get_control_states(name_ptr,
                                         &mut states,
                                         &mut nstates);
            if res != 0 {
                return Err("Failed to load control states");
            }
            let csvec: Vec<POETControlState> = Vec::from_raw_parts(states, nstates as usize, nstates as usize);
            // clone so we can free C-allocated memory (so user doesn't have to)
            let ret = csvec.clone();
            mem::forget(csvec);
            libc::free(states as *mut c_void);
            Ok(ret)
        }
    }
}

impl Default for POETControlState {
    fn default() -> POETControlState {
        POETControlState {
            id: 0,
            speedup: 1.0,
            cost: 1.0,
        }
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
/// Representation of native struct `poet_cpu_state_t`.
pub struct POETCpuState {
    id: c_uint,
    freq: c_uint,
    cores: c_uint,
}

impl POETCpuState {
    /// Attempt to load cpu states from a file.
    pub fn new(filename: Option<&CString>) -> Result<Vec<POETCpuState>, &'static str> {
        let name_ptr = match filename {
            Some(f) => f.as_ptr(),
            None => ptr::null(),
        };
        let mut states: *mut POETCpuState = ptr::null_mut::<POETCpuState>();
        unsafe {
            let mut nstates: u32 = 0;
            let res = get_cpu_states(name_ptr,
                                     &mut states,
                                     &mut nstates);
            if res != 0 {
                return Err("Failed to load cpu states");
            }
            let csvec: Vec<POETCpuState> = Vec::from_raw_parts(states, nstates as usize, nstates as usize);
            // clone so we can free C-allocated memory (so user doesn't have to)
            let ret = csvec.clone();
            mem::forget(csvec);
            libc::free(states as *mut c_void);
            Ok(ret)
        }
    }
}

impl Default for POETCpuState {
    fn default() -> POETCpuState {
        POETCpuState {
            id: 0,
            freq: 0,
            cores: 0,
        }
    }
}

/// The `POET` struct wraps an underyling C struct.
pub struct POET {
    /// The underlying C struct `poet_state`.
    pub poet: *mut c_void,
}

impl POET {
    /// Attempt to initialize POET and allocate resources in the underlying C struct.
    pub fn new(perf_goal: f64,
               control_states: &mut Vec<POETControlState>,
               cpu_states: &mut Vec<POETCpuState>,
               apply_func: Option<POETApplyFn>,
               curr_state_func: Option<POETCurrentStateFn>,
               period: u32,
               buffer_depth: u32,
               log_filename: Option<&CString>) -> Result<POET, &'static str> {
        if control_states.len() != cpu_states.len() {
            return Err("Number of control and cpu states don't match");
        }
        let apply_func = match apply_func {
            Some(p) => p,
            None => apply_cpu_config,
        };
        let curr_state_func = match curr_state_func {
            Some(p) => p,
            None => get_current_cpu_state,
        };
        let log_ptr = match log_filename {
            Some(l) => l.as_ptr(),
            None => ptr::null(),
        };
        let poet = unsafe {
            let num_states = control_states.len() as u32;
            poet_init(perf_goal,
                      num_states, control_states.as_mut_ptr(), cpu_states.as_mut_ptr(),
                      apply_func, curr_state_func,
                      period, buffer_depth, log_ptr)
        };
        if poet.is_null() {
            return Err("Failed to instantiate POET object");
        }
        Ok(POET { poet: poet, })
    }

    /// Call at every iteration - at specified periods this function will (potentially) order
    /// changes to system or application state to try and meet timing constraints.
    pub fn apply_control(&mut self, tag: u64, window_rate: f64, window_power: f64) {
        unsafe {
            poet_apply_control(self.poet, tag, window_rate, window_power);
        }
    }
}

impl Drop for POET {
    /// Cleanup POET and deallocate resources in the underlying C struct.
    fn drop(&mut self) {
        unsafe {
            poet_destroy(self.poet);
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use libc::{c_void, c_uint};
    use std::ffi::CString;

    #[test]
    fn test_basic() {
        let mut control_states = vec![POETControlState::default()];
        let mut cpu_states = vec![POETCpuState::default()];
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 None, None,
                                 20u32, 1u32, None).unwrap();
        poet.apply_control(0, 1.0, 1.0);
    }

    #[test]
    fn test_control_cpu_files_with_log() {
        let mut control_states = POETControlState::new(Some(&CString::new("test/control_config").unwrap())).unwrap();
        let mut cpu_states = POETCpuState::new(Some(&CString::new("test/cpu_config").unwrap())).unwrap();
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 None, None,
                                 20u32, 1u32, Some(&CString::new("poet.log").unwrap())).unwrap();
        poet.apply_control(0, 1.0, 1.0);
    }

    #[test]
    fn test_rust_callbacks() {
        let mut control_states = vec![POETControlState::default()];
        let mut cpu_states = vec![POETCpuState::default()];
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 Some(dummy_apply), Some(dummy_curr_state),
                                 20u32, 1u32, None).unwrap();
        for i in 0..50 {
            poet.apply_control(i, 1.0, 1.0);
        }
    }

    unsafe extern fn dummy_apply(_states: *mut c_void,
                                 _num_states: c_uint,
                                 _id: c_uint,
                                 _last_id: c_uint) {
        // do nothing
        println!("Received apply call");
    }

    unsafe extern fn dummy_curr_state (_states: *const c_void,
                                       _num_states: c_uint,
                                       _curr_state_id: *mut c_uint) -> i32 {
        println!("Received curr state call");
        // this is actually an invalid value, but forces the apply function to be called
        *_curr_state_id = _num_states;
        0
    }

}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std;
use std::{fmt, string};
use std::borrow::Cow;
use std::cmp::Ordering;
use ffi;
use libc;
use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, ToPythonPointer};
use objects::{PyObject, PyTuple, PyDict, PyString};
use conversion::ToPyObject;
use err::{PyErr, PyResult, result_from_owned_ptr, error_on_minusone};

/// Trait that contains methods
pub trait ObjectProtocol<'p> : PythonObject<'p> {
    /// Determines whether this object has the given attribute.
    /// This is equivalent to the Python expression 'hasattr(self, attr_name)'.
    #[inline]
    fn hasattr<N: ?Sized>(&self, attr_name: &N) -> PyResult<'p, bool> where N: ToPyObject<'p> {
        attr_name.with_borrowed_ptr(self.python(), |attr_name| unsafe {
            Ok(ffi::PyObject_HasAttr(self.as_ptr(), attr_name) != 0)
        })
    }

    /// Retrieves an attribute value.
    /// This is equivalent to the Python expression 'self.attr_name'.
    #[inline]
    fn getattr<N: ?Sized>(&self, attr_name: &N) -> PyResult<'p, PyObject<'p>> where N: ToPyObject<'p> {
        let py = self.python();
        attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
            result_from_owned_ptr(py,
                ffi::PyObject_GetAttr(self.as_ptr(), attr_name))
        })
    }

    /// Sets an attribute value.
    /// This is equivalent to the Python expression 'self.attr_name = value'.
    #[inline]
    fn setattr<N: ?Sized, V: ?Sized>(&self, attr_name: &N, value: &V) -> PyResult<'p, ()>
        where N: ToPyObject<'p>, V: ToPyObject<'p>
    {
        let py = self.python();
        attr_name.with_borrowed_ptr(py, move |attr_name|
            value.with_borrowed_ptr(py, |value| unsafe {
                error_on_minusone(py,
                    ffi::PyObject_SetAttr(self.as_ptr(), attr_name, value))
            }))
    }

    /// Deletes an attribute.
    /// This is equivalent to the Python expression 'del self.attr_name'.
    #[inline]
    fn delattr<N: ?Sized>(&self, attr_name: &N) -> PyResult<'p, ()> where N: ToPyObject<'p> {
        let py = self.python();
        attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
            error_on_minusone(py,
                ffi::PyObject_DelAttr(self.as_ptr(), attr_name))
        })
    }

    /// Compares two python objects.
    /// This is equivalent to the python expression 'cmp(self, other)'.
    #[cfg(feature="python27-sys")]
    fn compare<O: ?Sized>(&self, other: &O) -> PyResult<'p, Ordering> where O: ToPyObject<'p> {
        let py = self.python();
        other.with_borrowed_ptr(py, |other| unsafe {
            let mut result : libc::c_int = std::mem::uninitialized();
            try!(error_on_minusone(py,
                ffi::PyObject_Cmp(self.as_ptr(), other, &mut result)));
            Ok(if result < 0 {
                Ordering::Less
            } else if result > 0 {
                Ordering::Greater
            } else {
                Ordering::Equal
            })
        })
    }

    /// Compute the string representation of self.
    /// This is equivalent to the python expression 'repr(self)'.
    #[inline]
    fn repr(&self) -> PyResult<'p, PyObject<'p>> {
        unsafe {
            result_from_owned_ptr(self.python(), ffi::PyObject_Repr(self.as_ptr()))
        }
    }

    /// Compute the string representation of self.
    /// This is equivalent to the python expression 'str(self)'.
    #[inline]
    fn str(&self) -> PyResult<'p, PyObject<'p>> {
        unsafe {
            result_from_owned_ptr(self.python(), ffi::PyObject_Str(self.as_ptr()))
        }
    }

    /// Compute the unicode string representation of self.
    /// This is equivalent to the python expression 'unistr(self)'.
    #[inline]
    #[cfg(feature="python27-sys")]
    fn unistr(&self) -> PyResult<'p, PyObject<'p>> {
        unsafe {
            result_from_owned_ptr(self.python(), ffi::PyObject_Unicode(self.as_ptr()))
        }
    }

    /// Determines whether this object is callable.
    #[inline]
    fn is_callable(&self) -> bool {
        unsafe {
            ffi::PyCallable_Check(self.as_ptr()) != 0
        }
    }

    /// Calls the object.
    /// This is equivalent to the python expression: 'self(*args, **kwargs)'
    #[inline]
    fn call<A: ?Sized>(&self, args: &A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
      where A: ToPyObject<'p, ObjectType=PyTuple<'p>> {
        let py = self.python();
        args.with_borrowed_ptr(py, |args| unsafe {
            result_from_owned_ptr(py, ffi::PyObject_Call(self.as_ptr(), args, kwargs.as_ptr()))
        })
    }

    /// Calls a method on the object.
    /// This is equivalent to the python expression: 'self.name(*args, **kwargs)'
    #[inline]
    fn call_method<A: ?Sized>(&self, name: &str, args: &A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
      where A: ToPyObject<'p, ObjectType=PyTuple<'p>> {
        try!(self.getattr(name)).call(args, kwargs)
    }

    /// Retrieves the hash code of the object.
    /// This is equivalent to the python expression: 'hash(self)'
    #[inline]
    fn hash(&self) -> PyResult<'p, libc::c_long> {
        let v = unsafe { ffi::PyObject_Hash(self.as_ptr()) };
        if v == -1 {
            Err(PyErr::fetch(self.python()))
        } else {
            Ok(v)
        }
    }

    /// Returns whether the object is considered to be true.
    /// This is equivalent to the python expression: 'not not self'
    #[inline]
    fn is_true(&self) -> PyResult<'p, bool> {
        let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
        if v == -1 {
            Err(PyErr::fetch(self.python()))
        } else {
            Ok(v != 0)
        }
    }

    /// Returns the length of the sequence or mapping.
    /// This is equivalent to the python expression: 'len(self)'
    #[inline]
    fn len(&self) -> PyResult<'p, usize> {
        let v = unsafe { ffi::PyObject_Size(self.as_ptr()) };
        if v == -1 {
            Err(PyErr::fetch(self.python()))
        } else {
            Ok(v as usize)
        }
    }

    /// This is equivalent to the python expression: 'self[key]'
    #[inline]
    fn get_item<K>(&self, key: K) -> PyResult<'p, PyObject<'p>> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            result_from_owned_ptr(py,
                ffi::PyObject_GetItem(self.as_ptr(), key))
        })
    }

    /// Sets an item value.
    /// This is equivalent to the Python expression 'self[key] = value'.
    #[inline]
    fn set_item<K, V>(&self, key: K, value: V) -> PyResult<'p, ()> where K: ToPyObject<'p>, V: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, move |key|
            value.with_borrowed_ptr(py, |value| unsafe {
                error_on_minusone(py,
                    ffi::PyObject_SetItem(self.as_ptr(), key, value))
            }))
    }

    /// Deletes an item.
    /// This is equivalent to the Python expression 'del self[key]'.
    #[inline]
    fn del_item<K>(&self, key: K) -> PyResult<'p, ()> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            error_on_minusone(py,
                ffi::PyObject_DelItem(self.as_ptr(), key))
        })
    }
    /*
    /// Takes an object and returns an iterator for it.
    /// This is typically a new iterator but if the argument
    /// is an iterator, this returns itself.
    #[inline]
    fn iter(&self) -> PyResult<'p, PyPtr<'p, PyIterator<'p>>> {
        let it = try!(unsafe {
            result_from_owned_ptr(self.python(), ffi::PyObject_GetIter(self.as_ptr()))
        });
        it.downcast_into()
    }*/
}

impl <'p> ObjectProtocol<'p> for PyObject<'p> {}

/*
pub struct PyIterator<'p>(PyObject<'p>);

impl <'p> PythonObject<'p> for PyIterator<'p> {
    #[inline]
    fn as_object<'a>(&'a self) -> &'a PyObject<'p> {
        &self.0
    }

    #[inline]
    unsafe fn unchecked_downcast_from<'a>(o: &'a PyObject<'p>) -> &'a PyIterator<'p> {
        std::mem::transmute(o)
    }
}

*/

impl <'p> fmt::Debug for PyObject<'p> {
    fn fmt(&self, f : &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use objectprotocol::ObjectProtocol;
        let repr_obj = try!(self.str().map_err(|_| fmt::Error));
        let repr = try!(PyString::extract_lossy(&repr_obj).map_err(|_| fmt::Error));
        f.write_str(&*repr)
    }
}

impl <'p> fmt::Display for PyObject<'p> {
    fn fmt(&self, f : &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use objectprotocol::ObjectProtocol;
        let repr_obj = try!(self.repr().map_err(|_| fmt::Error));
        let repr = try!(PyString::extract_lossy(&repr_obj).map_err(|_| fmt::Error));
        f.write_str(&*repr)
    }
}

<commit_msg>Prefer taking generic arguments by value.<commit_after>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std;
use std::{fmt, string};
use std::borrow::Cow;
use std::cmp::Ordering;
use ffi;
use libc;
use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, ToPythonPointer};
use objects::{PyObject, PyTuple, PyDict, PyString};
use conversion::ToPyObject;
use err::{PyErr, PyResult, result_from_owned_ptr, error_on_minusone};

/// Trait that contains methods
pub trait ObjectProtocol<'p> : PythonObject<'p> {
    /// Determines whether this object has the given attribute.
    /// This is equivalent to the Python expression 'hasattr(self, attr_name)'.
    #[inline]
    fn hasattr<N>(&self, attr_name: N) -> PyResult<'p, bool> where N: ToPyObject<'p> {
        attr_name.with_borrowed_ptr(self.python(), |attr_name| unsafe {
            Ok(ffi::PyObject_HasAttr(self.as_ptr(), attr_name) != 0)
        })
    }

    /// Retrieves an attribute value.
    /// This is equivalent to the Python expression 'self.attr_name'.
    #[inline]
    fn getattr<N>(&self, attr_name: N) -> PyResult<'p, PyObject<'p>> where N: ToPyObject<'p> {
        let py = self.python();
        attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
            result_from_owned_ptr(py,
                ffi::PyObject_GetAttr(self.as_ptr(), attr_name))
        })
    }

    /// Sets an attribute value.
    /// This is equivalent to the Python expression 'self.attr_name = value'.
    #[inline]
    fn setattr<N, V>(&self, attr_name: N, value: V) -> PyResult<'p, ()>
        where N: ToPyObject<'p>, V: ToPyObject<'p>
    {
        let py = self.python();
        attr_name.with_borrowed_ptr(py, move |attr_name|
            value.with_borrowed_ptr(py, |value| unsafe {
                error_on_minusone(py,
                    ffi::PyObject_SetAttr(self.as_ptr(), attr_name, value))
            }))
    }

    /// Deletes an attribute.
    /// This is equivalent to the Python expression 'del self.attr_name'.
    #[inline]
    fn delattr<N>(&self, attr_name: N) -> PyResult<'p, ()> where N: ToPyObject<'p> {
        let py = self.python();
        attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
            error_on_minusone(py,
                ffi::PyObject_DelAttr(self.as_ptr(), attr_name))
        })
    }

    /// Compares two python objects.
    /// This is equivalent to the python expression 'cmp(self, other)'.
    #[cfg(feature="python27-sys")]
    fn compare<O>(&self, other: O) -> PyResult<'p, Ordering> where O: ToPyObject<'p> {
        let py = self.python();
        other.with_borrowed_ptr(py, |other| unsafe {
            let mut result : libc::c_int = -1;
            try!(error_on_minusone(py,
                ffi::PyObject_Cmp(self.as_ptr(), other, &mut result)));
            Ok(if result < 0 {
                Ordering::Less
            } else if result > 0 {
                Ordering::Greater
            } else {
                Ordering::Equal
            })
        })
    }

    /// Compute the string representation of self.
    /// This is equivalent to the python expression 'repr(self)'.
    #[inline]
    fn repr(&self) -> PyResult<'p, PyObject<'p>> {
        unsafe {
            result_from_owned_ptr(self.python(), ffi::PyObject_Repr(self.as_ptr()))
        }
    }

    /// Compute the string representation of self.
    /// This is equivalent to the python expression 'str(self)'.
    #[inline]
    fn str(&self) -> PyResult<'p, PyObject<'p>> {
        unsafe {
            result_from_owned_ptr(self.python(), ffi::PyObject_Str(self.as_ptr()))
        }
    }

    /// Compute the unicode string representation of self.
    /// This is equivalent to the python expression 'unistr(self)'.
    #[inline]
    #[cfg(feature="python27-sys")]
    fn unistr(&self) -> PyResult<'p, PyObject<'p>> {
        unsafe {
            result_from_owned_ptr(self.python(), ffi::PyObject_Unicode(self.as_ptr()))
        }
    }

    /// Determines whether this object is callable.
    #[inline]
    fn is_callable(&self) -> bool {
        unsafe {
            ffi::PyCallable_Check(self.as_ptr()) != 0
        }
    }

    /// Calls the object.
    /// This is equivalent to the python expression: 'self(*args, **kwargs)'
    #[inline]
    fn call<A>(&self, args: A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
      where A: ToPyObject<'p, ObjectType=PyTuple<'p>> {
        let py = self.python();
        args.with_borrowed_ptr(py, |args| unsafe {
            result_from_owned_ptr(py, ffi::PyObject_Call(self.as_ptr(), args, kwargs.as_ptr()))
        })
    }

    /// Calls a method on the object.
    /// This is equivalent to the python expression: 'self.name(*args, **kwargs)'
    #[inline]
    fn call_method<A>(&self, name: &str, args: A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
      where A: ToPyObject<'p, ObjectType=PyTuple<'p>> {
        try!(self.getattr(name)).call(args, kwargs)
    }

    /// Retrieves the hash code of the object.
    /// This is equivalent to the python expression: 'hash(self)'
    #[inline]
    fn hash(&self) -> PyResult<'p, libc::c_long> {
        let v = unsafe { ffi::PyObject_Hash(self.as_ptr()) };
        if v == -1 {
            Err(PyErr::fetch(self.python()))
        } else {
            Ok(v)
        }
    }

    /// Returns whether the object is considered to be true.
    /// This is equivalent to the python expression: 'not not self'
    #[inline]
    fn is_true(&self) -> PyResult<'p, bool> {
        let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
        if v == -1 {
            Err(PyErr::fetch(self.python()))
        } else {
            Ok(v != 0)
        }
    }

    /// Returns the length of the sequence or mapping.
    /// This is equivalent to the python expression: 'len(self)'
    #[inline]
    fn len(&self) -> PyResult<'p, usize> {
        let v = unsafe { ffi::PyObject_Size(self.as_ptr()) };
        if v == -1 {
            Err(PyErr::fetch(self.python()))
        } else {
            Ok(v as usize)
        }
    }

    /// This is equivalent to the python expression: 'self[key]'
    #[inline]
    fn get_item<K>(&self, key: K) -> PyResult<'p, PyObject<'p>> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            result_from_owned_ptr(py,
                ffi::PyObject_GetItem(self.as_ptr(), key))
        })
    }

    /// Sets an item value.
    /// This is equivalent to the Python expression 'self[key] = value'.
    #[inline]
    fn set_item<K, V>(&self, key: K, value: V) -> PyResult<'p, ()> where K: ToPyObject<'p>, V: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, move |key|
            value.with_borrowed_ptr(py, |value| unsafe {
                error_on_minusone(py,
                    ffi::PyObject_SetItem(self.as_ptr(), key, value))
            }))
    }

    /// Deletes an item.
    /// This is equivalent to the Python expression 'del self[key]'.
    #[inline]
    fn del_item<K>(&self, key: K) -> PyResult<'p, ()> where K: ToPyObject<'p> {
        let py = self.python();
        key.with_borrowed_ptr(py, |key| unsafe {
            error_on_minusone(py,
                ffi::PyObject_DelItem(self.as_ptr(), key))
        })
    }
    /*
    /// Takes an object and returns an iterator for it.
    /// This is typically a new iterator but if the argument
    /// is an iterator, this returns itself.
    #[inline]
    fn iter(&self) -> PyResult<'p, PyPtr<'p, PyIterator<'p>>> {
        let it = try!(unsafe {
            result_from_owned_ptr(self.python(), ffi::PyObject_GetIter(self.as_ptr()))
        });
        it.downcast_into()
    }*/
}

impl <'p> ObjectProtocol<'p> for PyObject<'p> {}

/*
pub struct PyIterator<'p>(PyObject<'p>);

impl <'p> PythonObject<'p> for PyIterator<'p> {
    #[inline]
    fn as_object<'a>(&'a self) -> &'a PyObject<'p> {
        &self.0
    }

    #[inline]
    unsafe fn unchecked_downcast_from<'a>(o: &'a PyObject<'p>) -> &'a PyIterator<'p> {
        std::mem::transmute(o)
    }
}

*/

impl <'p> fmt::Debug for PyObject<'p> {
    fn fmt(&self, f : &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use objectprotocol::ObjectProtocol;
        let repr_obj = try!(self.str().map_err(|_| fmt::Error));
        let repr = try!(PyString::extract_lossy(&repr_obj).map_err(|_| fmt::Error));
        f.write_str(&*repr)
    }
}

impl <'p> fmt::Display for PyObject<'p> {
    fn fmt(&self, f : &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use objectprotocol::ObjectProtocol;
        let repr_obj = try!(self.repr().map_err(|_| fmt::Error));
        let repr = try!(PyString::extract_lossy(&repr_obj).map_err(|_| fmt::Error));
        f.write_str(&*repr)
    }
}

<|endoftext|>