<commit_before>#![feature(plugin_registrar, macro_rules)]

extern crate rustc;
extern crate syntax;

use std::io::Command;
use std::io::fs::PathExtensions;
use std::os;
use std::str;
use std::sync::{Once, ONCE_INIT};

use rustc::plugin::Registry;
use syntax::abi;
use syntax::ast;
use syntax::attr;
use syntax::ptr::P;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, DummyResult};
use syntax::ext::base::MacResult;
use syntax::ext::build::AstBuilder;
use syntax::fold::Folder;
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,
    state: State,
    deps: Vec<(String, bool)>, // (name, static)
    locs: Vec<Path>,
}

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

enum State { Dynamic, Static(SystemDeps) }
enum SystemDeps { SystemDynamic, SystemStatic }
enum Favor { FavorDynamic, FavorStatic }

#[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+'static> {
    macro_rules! try_dummy( ($e:expr) => (
        match $e { Ok(s) => s, Err(()) => return DummyResult::any(span) }
    ) )

    let mut parser = ecx.new_parser_from_tts(tts);
    let (pkg, sp) = try_dummy!(parse_string(ecx, &mut parser));
    let mut favor_dynamic = FavorDynamic;
    let mut dylib_state = Some(Dynamic);
    let mut static_state = Some(Static(SystemDynamic));
    if parser.eat(&token::Comma) && parser.eat(&token::LBracket) {
        while !parser.eat(&token::RBracket) {
            parser.eat(&token::Comma);
            let (modifier, sp) = try_dummy!(parse_string(ecx, &mut parser));
            match modifier.as_slice() {
                "only_static" => {
                    dylib_state = None;
                    favor_dynamic = FavorStatic;
                }
                "only_dylib" => {
                    static_state = None;
                    favor_dynamic = FavorDynamic;
                }
                "system_static" => static_state = Some(Static(SystemStatic)),
                "favor_static" => favor_dynamic = FavorStatic,
                s => ecx.span_err(sp, format!("unknown modifier: `{}`",
                                              s).as_slice()),
            }
        }
    }
    if !parser.eat(&token::Eof) {
        ecx.span_err(parser.span, "only one string literal allowed");
        return DummyResult::any(span);
    }

    let dyn = try_dummy!(system_pkgconfig(ecx, sp, pkg.as_slice(), dylib_state));
    let statik = try_dummy!(system_pkgconfig(ecx, sp, pkg.as_slice(), static_state));

    let mut items = Vec::new();
    match dyn {
        Some(info) => items.push(block(ecx, sp, &info, favor_dynamic)),
        None => {}
    }
    match statik {
        Some(info) => items.push(block(ecx, sp, &info, favor_dynamic)),
        None => {}
    }
    box MacItems { items: items } as Box<MacResult>
}

fn system_pkgconfig(ecx: &mut ExtCtxt, sp: Span, pkg: &str,
                    state: Option<State>) -> Result<Option<LibInfo>, ()> {
    let state = match state {
        Some(state) => state,
        None => return Ok(None),
    };
    add_cargo_pkg_config_paths();

    let mut cmd = Command::new("pkg-config");
    cmd.arg(pkg).arg("--libs").env("PKG_CONFIG_ALLOW_SYSTEM_LIBS", "1");
    match state {
        Static(..) => { cmd.arg("--static"); }
        Dynamic => {}
    }
    let out = match cmd.output() {
        Ok(out) => out,
        Err(e) => {
            ecx.span_err(sp, format!("could not run pkg-config: {}", e).as_slice());
            return Err(())
        }
    };
    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 Err(())
    }

    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(Path::new(arg.slice_from(2).to_string()));
        }
    }

    let allow_static = match state {
        Static(..) => true,
        _ => false,
    };
    let allow_static_system = match state {
        Static(SystemStatic) => true,
        _ => false,
    };

    let cargo_locs = cargo_native_dirs();
    let libs = libs.into_iter().map(|lib| {
        let mut candidates = cargo_locs.iter().chain(locs.iter());
        (lib.to_string(), allow_static && candidates.any(|base| {
            (allow_static_system || !base.as_vec().starts_with(b"/usr")) &&
                (base.join(format!("lib{}.a", lib)).exists() ||
                 base.join(format!("{}.lib", lib)).exists() ||
                 base.join(format!("lib{}.lib", lib)).exists())
        }))
    }).collect();
    Ok(Some(LibInfo {
        lib: pkg.to_string(),
        deps: libs,
        locs: locs,
        state: state,
    }))
}

fn block(ecx: &mut ExtCtxt, sp: Span, info: &LibInfo,
         favor: Favor) -> P<ast::Item> {
    let lib = token::intern_and_get_ident(info.lib.as_slice());
    let s = match favor {
        FavorDynamic => InternedString::new("statik"),
        FavorStatic => InternedString::new("dylib"),
    };
    let cfg = ecx.meta_name_value(sp, s, ast::LitStr(lib, ast::CookedStr));
    let cfg = match (info.state, favor) {
        (Static(..), FavorDynamic) |
        (Dynamic, FavorStatic) => cfg,
        (Dynamic, FavorDynamic) |
        (Static(..), FavorStatic) => {
            ecx.meta_list(sp, InternedString::new("not"), vec![cfg])
        }
    };
    let cfg = ecx.meta_list(sp, InternedString::new("cfg"), vec![cfg]);

    let attrs = vec![ecx.attribute(sp, cfg)].into_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.display()).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) -> Result<(String, Span), ()> {
    let entry = ecx.expander().fold_expr(parser.parse_expr());
    match entry.node {
        ast::ExprLit(ref lit) => {
            match lit.node {
                ast::LitStr(ref s, _) => return Ok((s.to_string(), entry.span)),
                _ => {}
            }
        }
        _ => {}
    }
    ecx.span_err(entry.span, format!(
        "expected string literal but got `{}`",
        pprust::expr_to_string(&*entry)).as_slice());
    Err(())
}

impl MacResult for MacItems {
    fn make_items(self: Box<MacItems>) -> Option<SmallVector<P<ast::Item>>> {
        Some(self.items.into_iter().collect())
    }
}

// lol hax
fn cargo_native_dirs() -> Vec<Path> {
    match rustc::driver::handle_options(os::args()) {
        Some(matches) => {
            matches.opt_strs("L").into_iter().filter_map(|s| {
                if s.as_slice().contains("native") {
                    Some(Path::new(s))
                } else {
                    None
                }
            }).collect()
        }
        None => Vec::new()
    }
}

fn add_cargo_pkg_config_paths() {
    static mut DONE: Once = ONCE_INIT;
    unsafe { DONE.doit(add_cargo_pkg_config_paths) }

    fn add_cargo_pkg_config_paths() {
        let path = os::getenv_as_bytes("PKG_CONFIG_PATH").unwrap_or(Vec::new());
        let mut pkg_config_path = os::split_paths(path.as_slice());
        pkg_config_path.push_all(cargo_native_dirs().as_slice());
        let pkg_config_path = os::join_paths(pkg_config_path.as_slice())
                                 .unwrap();
        os::setenv("PKG_CONFIG_PATH", pkg_config_path.as_slice());
    }
}
<commit_msg>Update for latest rust<commit_after>#![feature(plugin_registrar, macro_rules)]

extern crate rustc;
extern crate syntax;

use std::io::Command;
use std::io::fs::PathExtensions;
use std::os;
use std::str;
use std::sync::{Once, ONCE_INIT};

use rustc::plugin::Registry;
use syntax::abi;
use syntax::ast;
use syntax::attr;
use syntax::ptr::P;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, DummyResult};
use syntax::ext::base::MacResult;
use syntax::ext::build::AstBuilder;
use syntax::fold::Folder;
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,
    state: State,
    deps: Vec<(String, bool)>, // (name, static)
    locs: Vec<Path>,
}

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

enum State { Dynamic, Static(SystemDeps) }
enum SystemDeps { SystemDynamic, SystemStatic }
enum Favor { FavorDynamic, FavorStatic }

#[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+'static> {
    macro_rules! try_dummy( ($e:expr) => (
        match $e { Ok(s) => s, Err(()) => return DummyResult::any(span) }
    ) )

    let mut parser = ecx.new_parser_from_tts(tts);
    let (pkg, sp) = try_dummy!(parse_string(ecx, &mut parser));
    let mut favor_dynamic = FavorDynamic;
    let mut dylib_state = Some(Dynamic);
    let mut static_state = Some(Static(SystemDynamic));
    if parser.eat(&token::Comma) && parser.eat(&token::OpenDelim(token::Bracket)) {
        while !parser.eat(&token::OpenDelim(token::Bracket)) {
            parser.eat(&token::Comma);
            let (modifier, sp) = try_dummy!(parse_string(ecx, &mut parser));
            match modifier.as_slice() {
                "only_static" => {
                    dylib_state = None;
                    favor_dynamic = FavorStatic;
                }
                "only_dylib" => {
                    static_state = None;
                    favor_dynamic = FavorDynamic;
                }
                "system_static" => static_state = Some(Static(SystemStatic)),
                "favor_static" => favor_dynamic = FavorStatic,
                s => ecx.span_err(sp, format!("unknown modifier: `{}`",
                                              s).as_slice()),
            }
        }
    }
    if !parser.eat(&token::Eof) {
        ecx.span_err(parser.span, "only one string literal allowed");
        return DummyResult::any(span);
    }

    let dyn = try_dummy!(system_pkgconfig(ecx, sp, pkg.as_slice(), dylib_state));
    let statik = try_dummy!(system_pkgconfig(ecx, sp, pkg.as_slice(), static_state));

    let mut items = Vec::new();
    match dyn {
        Some(info) => items.push(block(ecx, sp, &info, favor_dynamic)),
        None => {}
    }
    match statik {
        Some(info) => items.push(block(ecx, sp, &info, favor_dynamic)),
        None => {}
    }
    box MacItems { items: items } as Box<MacResult>
}

fn system_pkgconfig(ecx: &mut ExtCtxt, sp: Span, pkg: &str,
                    state: Option<State>) -> Result<Option<LibInfo>, ()> {
    let state = match state {
        Some(state) => state,
        None => return Ok(None),
    };
    add_cargo_pkg_config_paths();

    let mut cmd = Command::new("pkg-config");
    cmd.arg(pkg).arg("--libs").env("PKG_CONFIG_ALLOW_SYSTEM_LIBS", "1");
    match state {
        Static(..) => { cmd.arg("--static"); }
        Dynamic => {}
    }
    let out = match cmd.output() {
        Ok(out) => out,
        Err(e) => {
            ecx.span_err(sp, format!("could not run pkg-config: {}", e).as_slice());
            return Err(())
        }
    };
    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 Err(())
    }

    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(Path::new(arg.slice_from(2).to_string()));
        }
    }

    let allow_static = match state {
        Static(..) => true,
        _ => false,
    };
    let allow_static_system = match state {
        Static(SystemStatic) => true,
        _ => false,
    };

    let cargo_locs = cargo_native_dirs();
    let libs = libs.into_iter().map(|lib| {
        let mut candidates = cargo_locs.iter().chain(locs.iter());
        (lib.to_string(), allow_static && candidates.any(|base| {
            (allow_static_system || !base.as_vec().starts_with(b"/usr")) &&
                (base.join(format!("lib{}.a", lib)).exists() ||
                 base.join(format!("{}.lib", lib)).exists() ||
                 base.join(format!("lib{}.lib", lib)).exists())
        }))
    }).collect();
    Ok(Some(LibInfo {
        lib: pkg.to_string(),
        deps: libs,
        locs: locs,
        state: state,
    }))
}

fn block(ecx: &mut ExtCtxt, sp: Span, info: &LibInfo,
         favor: Favor) -> P<ast::Item> {
    let lib = token::intern_and_get_ident(info.lib.as_slice());
    let s = match favor {
        FavorDynamic => InternedString::new("statik"),
        FavorStatic => InternedString::new("dylib"),
    };
    let cfg = ecx.meta_name_value(sp, s, ast::LitStr(lib, ast::CookedStr));
    let cfg = match (info.state, favor) {
        (Static(..), FavorDynamic) |
        (Dynamic, FavorStatic) => cfg,
        (Dynamic, FavorDynamic) |
        (Static(..), FavorStatic) => {
            ecx.meta_list(sp, InternedString::new("not"), vec![cfg])
        }
    };
    let cfg = ecx.meta_list(sp, InternedString::new("cfg"), vec![cfg]);

    let attrs = vec![ecx.attribute(sp, cfg)].into_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.display()).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) -> Result<(String, Span), ()> {
    let entry = ecx.expander().fold_expr(parser.parse_expr());
    match entry.node {
        ast::ExprLit(ref lit) => {
            match lit.node {
                ast::LitStr(ref s, _) => return Ok((s.to_string(), entry.span)),
                _ => {}
            }
        }
        _ => {}
    }
    ecx.span_err(entry.span, format!(
        "expected string literal but got `{}`",
        pprust::expr_to_string(&*entry)).as_slice());
    Err(())
}

impl MacResult for MacItems {
    fn make_items(self: Box<MacItems>) -> Option<SmallVector<P<ast::Item>>> {
        Some(self.items.into_iter().collect())
    }
}

// lol hax
fn cargo_native_dirs() -> Vec<Path> {
    match rustc::driver::handle_options(os::args()) {
        Some(matches) => {
            matches.opt_strs("L").into_iter().filter_map(|s| {
                if s.as_slice().contains("native") {
                    Some(Path::new(s))
                } else {
                    None
                }
            }).collect()
        }
        None => Vec::new()
    }
}

fn add_cargo_pkg_config_paths() {
    static mut DONE: Once = ONCE_INIT;
    unsafe { DONE.doit(add_cargo_pkg_config_paths) }

    fn add_cargo_pkg_config_paths() {
        let path = os::getenv_as_bytes("PKG_CONFIG_PATH").unwrap_or(Vec::new());
        let mut pkg_config_path = os::split_paths(path.as_slice());
        pkg_config_path.push_all(cargo_native_dirs().as_slice());
        let pkg_config_path = os::join_paths(pkg_config_path.as_slice())
                                 .unwrap();
        os::setenv("PKG_CONFIG_PATH", pkg_config_path.as_slice());
    }
}
<|endoftext|>


<commit_before>// Module for parsing ISO Base Media Format aka video/mp4 streams.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

/// Basic ISO box structure.
pub struct BoxHeader {
    /// Four character box type
pub name: u32,
    /// Size of the box in bytes
pub size: u64,
    /// Offset to the start of the contained data (or header size).
pub offset: u64,
}

/// File type box 'ftyp'.
pub struct FileTypeBox {
    name: u32,
    size: u64,
    major_brand: u32,
    minor_version: u32,
    compatible_brands: Vec<u32>,
}

/// Movie header box 'mvhd'.
pub struct MovieHeaderBox {
    pub name: u32,
    pub size: u64,
    pub timescale: u32,
    pub duration: u64,
    // Ignore other fields.
}

extern crate byteorder;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Result, Seek, SeekFrom};

/// Parse a box out of a data buffer.
pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Option<BoxHeader> {
    let tmp_size = src.read_u32::<BigEndian>().unwrap();
    let name = src.read_u32::<BigEndian>().unwrap();
    let size = match tmp_size {
        1 => src.read_u64::<BigEndian>().unwrap(),
        _ => tmp_size as u64,
    };
    assert!(size >= 8);
    if tmp_size == 1 {
        assert!(size >= 16);
    }
    let offset = match tmp_size {
        1 => 4 + 4 + 8,
        _ => 4 + 4,
    };
    assert!(offset <= size);
    Some(BoxHeader{
        name: name,
        size: size,
        offset: offset,
    })
}

/// Parse the extra header fields for a full box.
fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> (u8, u32) {
    let version = src.read_u8().unwrap();
    let flags_a = src.read_u8().unwrap();
    let flags_b = src.read_u8().unwrap();
    let flags_c = src.read_u8().unwrap();
    (version, (flags_a as u32) << 16 |
              (flags_b as u32) <<  8 |
              (flags_c as u32))
}

/// Skip over the contents of a box.
pub fn skip_box_content<T: ReadBytesExt + Seek>
  (src: &mut T, header: &BoxHeader)
  -> std::io::Result<u64>
{
    src.seek(SeekFrom::Current((header.size - header.offset) as i64))
}

/// Parse an ftype box.
pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Option<FileTypeBox> {
    let major = src.read_u32::<BigEndian>().unwrap();
    let minor = src.read_u32::<BigEndian>().unwrap();
    let brand_count = (head.size - 8 - 8) /4;
    let mut brands = Vec::new();
    for _ in 0..brand_count {
        brands.push(src.read_u32::<BigEndian>().unwrap());
    }
    Some(FileTypeBox{
        name: head.name,
        size: head.size,
        major_brand: major,
        minor_version: minor,
        compatible_brands: brands,
    })
}

/// Parse an mvhd box.
pub fn read_mvhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Option<MovieHeaderBox> {
    let (version, _) = read_fullbox_extra(src);
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = src.read(&mut skip).unwrap();
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = src.read(&mut skip).unwrap();
            assert!(r == skip.len());
        },
        _ => panic!("invalid mhdr version"),
    }
    let timescale = src.read_u32::<BigEndian>().unwrap();
    let duration = match version {
        1 => src.read_u64::<BigEndian>().unwrap(),
        0 => src.read_u32::<BigEndian>().unwrap() as u64,
        _ => panic!("invalid mhdr version"),
    };
    // Skip remaining fields.
    let mut skip: Vec<u8> = vec![0; 80];
    let r = src.read(&mut skip).unwrap();
    assert!(r == skip.len());
    Some(MovieHeaderBox {
        name: head.name,
        size: head.size,
        timescale: timescale,
        duration: duration,
    })
}

/// Convert the iso box type or other 4-character value to a string.
pub fn fourcc_to_string(name: u32) -> String {
    let u32_to_vec = |u| {
        vec!((u >> 24 & 0xffu32) as u8,
             (u >> 16 & 0xffu32) as u8,
             (u >>  8 & 0xffu32) as u8,
             (u & 0xffu32) as u8)
    };
    let name_bytes = u32_to_vec(name);
    String::from_utf8_lossy(&name_bytes).into_owned()
}

use std::fmt;
impl fmt::Display for BoxHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "'{}' {} bytes", fourcc_to_string(self.name), self.size)
    }
}

impl fmt::Display for FileTypeBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        let brand = fourcc_to_string(self.major_brand);
        write!(f, "'{}' {} bytes '{}' v{}", name, self.size,
            brand, self.minor_version)
    }
}

impl fmt::Display for MovieHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        write!(f, "'{}' {} bytes duration {}s", name, self.size,
            (self.duration as f64)/(self.timescale as f64))
    }
}

#[test]
fn test_read_box_header() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 8];  // minimal box length
    write!(&mut test, "test").unwrap(); // box type
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1952805748);
    assert_eq!(parsed.size, 8);
    println!("box {}", parsed);
}

#[test]
fn test_read_box_header_long() {
    use std::io::Cursor;
    let mut test: Vec<u8> = vec![0, 0, 0, 1]; // long box extension code
    test.extend("long".to_string().into_bytes()); // box type
    test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]); // 64 bit size
    // Skip generating box content.
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1819242087);
    assert_eq!(parsed.size, 4096);
    println!("box {}", parsed);
}

#[test]
fn test_read_ftyp() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 24]; // size
    write!(&mut test, "ftyp").unwrap(); // type
    write!(&mut test, "mp42").unwrap(); // major brand
    test.extend(vec![0, 0, 0, 0]);      // minor version
    write!(&mut test, "isom").unwrap(); // compatible brands...
    write!(&mut test, "mp42").unwrap();
    assert_eq!(test.len(), 24);

    let mut stream = Cursor::new(test);
    let header = read_box_header(&mut stream).unwrap();
    let parsed = read_ftyp(&mut stream, &header).unwrap();
    assert_eq!(parsed.name, 1718909296);
    assert_eq!(parsed.size, 24);
    assert_eq!(parsed.major_brand, 1836069938);
    assert_eq!(parsed.minor_version, 0);
    assert_eq!(parsed.compatible_brands.len(), 2);
    println!("box {}", parsed);
}
<commit_msg>Add parser for tkdh boxes.<commit_after>// Module for parsing ISO Base Media Format aka video/mp4 streams.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

/// Basic ISO box structure.
pub struct BoxHeader {
    /// Four character box type
pub name: u32,
    /// Size of the box in bytes
pub size: u64,
    /// Offset to the start of the contained data (or header size).
pub offset: u64,
}

/// File type box 'ftyp'.
pub struct FileTypeBox {
    name: u32,
    size: u64,
    major_brand: u32,
    minor_version: u32,
    compatible_brands: Vec<u32>,
}

/// Movie header box 'mvhd'.
pub struct MovieHeaderBox {
    pub name: u32,
    pub size: u64,
    pub timescale: u32,
    pub duration: u64,
    // Ignore other fields.
}

pub struct TrackHeaderBox {
    pub name: u32,
    pub size: u64,
    pub track_id: u32,
    pub duration: u64,
    pub width: u32,
    pub height: u32,
}

extern crate byteorder;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Result, Seek, SeekFrom};

/// Parse a box out of a data buffer.
pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Option<BoxHeader> {
    let tmp_size = src.read_u32::<BigEndian>().unwrap();
    let name = src.read_u32::<BigEndian>().unwrap();
    let size = match tmp_size {
        1 => src.read_u64::<BigEndian>().unwrap(),
        _ => tmp_size as u64,
    };
    assert!(size >= 8);
    if tmp_size == 1 {
        assert!(size >= 16);
    }
    let offset = match tmp_size {
        1 => 4 + 4 + 8,
        _ => 4 + 4,
    };
    assert!(offset <= size);
    Some(BoxHeader{
        name: name,
        size: size,
        offset: offset,
    })
}

/// Parse the extra header fields for a full box.
fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> (u8, u32) {
    let version = src.read_u8().unwrap();
    let flags_a = src.read_u8().unwrap();
    let flags_b = src.read_u8().unwrap();
    let flags_c = src.read_u8().unwrap();
    (version, (flags_a as u32) << 16 |
              (flags_b as u32) <<  8 |
              (flags_c as u32))
}

/// Skip over the contents of a box.
pub fn skip_box_content<T: ReadBytesExt + Seek>
  (src: &mut T, header: &BoxHeader)
  -> std::io::Result<u64>
{
    src.seek(SeekFrom::Current((header.size - header.offset) as i64))
}

/// Parse an ftype box.
pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Option<FileTypeBox> {
    let major = src.read_u32::<BigEndian>().unwrap();
    let minor = src.read_u32::<BigEndian>().unwrap();
    let brand_count = (head.size - 8 - 8) /4;
    let mut brands = Vec::new();
    for _ in 0..brand_count {
        brands.push(src.read_u32::<BigEndian>().unwrap());
    }
    Some(FileTypeBox{
        name: head.name,
        size: head.size,
        major_brand: major,
        minor_version: minor,
        compatible_brands: brands,
    })
}

/// Parse an mvhd box.
pub fn read_mvhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Option<MovieHeaderBox> {
    let (version, _) = read_fullbox_extra(src);
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = src.read(&mut skip).unwrap();
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = src.read(&mut skip).unwrap();
            assert!(r == skip.len());
        },
        _ => panic!("invalid mhdr version"),
    }
    let timescale = src.read_u32::<BigEndian>().unwrap();
    let duration = match version {
        1 => src.read_u64::<BigEndian>().unwrap(),
        0 => src.read_u32::<BigEndian>().unwrap() as u64,
        _ => panic!("invalid mhdr version"),
    };
    // Skip remaining fields.
    let mut skip: Vec<u8> = vec![0; 80];
    let r = src.read(&mut skip).unwrap();
    assert!(r == skip.len());
    Some(MovieHeaderBox {
        name: head.name,
        size: head.size,
        timescale: timescale,
        duration: duration,
    })
}

/// Parse a tkhd box.
pub fn read_tkhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
  -> Option<TrackHeaderBox> {
    let (version, flags) = read_fullbox_extra(src);
    if flags & 0x1u32 == 0 || flags & 0x2u32 == 0 {
        // Track is disabled.
        return None;
    }
    match version {
        1 => {
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 16];
            let r = src.read(&mut skip).unwrap();
            assert!(r == skip.len());
        },
        0 => {
            // 32 bit creation and modification times.
            // 64 bit creation and modification times.
            let mut skip: Vec<u8> = vec![0; 8];
            let r = src.read(&mut skip).unwrap();
            assert!(r == skip.len());
        },
        _ => panic!("invalid tkhd version"),
    }
    let track_id = src.read_u32::<BigEndian>().unwrap();
    let _reserved = src.read_u32::<BigEndian>().unwrap();
    assert!(_reserved == 0);
    let duration = match version {
        1 => {
            src.read_u64::<BigEndian>().unwrap()
        },
        0 => src.read_u32::<BigEndian>().unwrap() as u64,
        _ => panic!("invalid tkhd version"),
    };
    let _reserved = src.read_u32::<BigEndian>().unwrap();
    let _reserved = src.read_u32::<BigEndian>().unwrap();
    // Skip uninterested fields.
    let mut skip: Vec<u8> = vec![0; 44];
    let r = src.read(&mut skip).unwrap();
    assert!(r == skip.len());
    let width = src.read_u32::<BigEndian>().unwrap();
    let height = src.read_u32::<BigEndian>().unwrap();
    Some(TrackHeaderBox {
        name: head.name,
        size: head.size,
        track_id: track_id,
        duration: duration,
        width: width,
        height: height,
    })
}

/// Convert the iso box type or other 4-character value to a string.
pub fn fourcc_to_string(name: u32) -> String {
    let u32_to_vec = |u| {
        vec!((u >> 24 & 0xffu32) as u8,
             (u >> 16 & 0xffu32) as u8,
             (u >>  8 & 0xffu32) as u8,
             (u & 0xffu32) as u8)
    };
    let name_bytes = u32_to_vec(name);
    String::from_utf8_lossy(&name_bytes).into_owned()
}

use std::fmt;
impl fmt::Display for BoxHeader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "'{}' {} bytes", fourcc_to_string(self.name), self.size)
    }
}

impl fmt::Display for FileTypeBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        let brand = fourcc_to_string(self.major_brand);
        write!(f, "'{}' {} bytes '{}' v{}", name, self.size,
            brand, self.minor_version)
    }
}

impl fmt::Display for MovieHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        write!(f, "'{}' {} bytes duration {}s", name, self.size,
            (self.duration as f64)/(self.timescale as f64))
    }
}

impl fmt::Display for TrackHeaderBox {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = fourcc_to_string(self.name);
        write!(f, "'{}' {} bytes id {} {}x{}",
            name, self.size, self.track_id,
            self.width, self.height)
    }
}

#[test]
fn test_read_box_header() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 8];  // minimal box length
    write!(&mut test, "test").unwrap(); // box type
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1952805748);
    assert_eq!(parsed.size, 8);
    println!("box {}", parsed);
}

#[test]
fn test_read_box_header_long() {
    use std::io::Cursor;
    let mut test: Vec<u8> = vec![0, 0, 0, 1]; // long box extension code
    test.extend("long".to_string().into_bytes()); // box type
    test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]); // 64 bit size
    // Skip generating box content.
    let mut stream = Cursor::new(test);
    let parsed = read_box_header(&mut stream).unwrap();
    assert_eq!(parsed.name, 1819242087);
    assert_eq!(parsed.size, 4096);
    println!("box {}", parsed);
}

#[test]
fn test_read_ftyp() {
    use std::io::Cursor;
    use std::io::Write;
    let mut test: Vec<u8> = vec![0, 0, 0, 24]; // size
    write!(&mut test, "ftyp").unwrap(); // type
    write!(&mut test, "mp42").unwrap(); // major brand
    test.extend(vec![0, 0, 0, 0]);      // minor version
    write!(&mut test, "isom").unwrap(); // compatible brands...
    write!(&mut test, "mp42").unwrap();
    assert_eq!(test.len(), 24);

    let mut stream = Cursor::new(test);
    let header = read_box_header(&mut stream).unwrap();
    let parsed = read_ftyp(&mut stream, &header).unwrap();
    assert_eq!(parsed.name, 1718909296);
    assert_eq!(parsed.size, 24);
    assert_eq!(parsed.major_brand, 1836069938);
    assert_eq!(parsed.minor_version, 0);
    assert_eq!(parsed.compatible_brands.len(), 2);
    println!("box {}", parsed);
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate bincode;
extern crate take_mut;

use std::io::{self, Read, Seek, Write};
use std::error::Error;
use std::marker::PhantomData;
use std::cell;

#[derive(Debug)]
pub enum DecodingError {
    Corrupt(String),
    IoError(io::Error),
}

fn decode<O: serde::de::DeserializeOwned, R: Read+Seek>(reader: &mut R, offset: u64) -> Result<O, DecodingError> {
    reader.seek(io::SeekFrom::Start(offset)).map_err(|x| DecodingError::IoError(x))?;
    bincode::deserialize_from(    reader, bincode::Infinite)
    .map_err(|x| {
        match *x {
            bincode::ErrorKind::IoError(y) => DecodingError::IoError(y),
            _ => DecodingError::Corrupt(x.description().to_string()),
        }
    })
}

#[derive(Debug)]
pub enum EncodingError {
    Unknown(String),
    IoError(io::Error),
}

fn encode<O: serde::Serialize, W: Write+Seek>(writer: &mut W, obj: &O) -> Result<u64, EncodingError> {
    let offset = writer.seek(io::SeekFrom::End(0)).map_err(|x| EncodingError::IoError(x))?;
    bincode::serialize_into(writer, obj, bincode::Infinite)
    .map_err(|x| {
        match *x {
            bincode::ErrorKind::IoError(y) => EncodingError::IoError(y),
            _ => EncodingError::Unknown(x.description().to_string()),
        }
    }).map(|_| offset)
}

#[derive(Serialize, Deserialize, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
enum NodeType {
    Root,
    Internal,
    Leaf
}

enum NodeRefInternal<K> {
    Unloaded(u64),
    Loaded(Box<Node<K>>),
}

struct NodeRef<K>(cell::UnsafeCell<NodeRefInternal<K>>);

// An on-disk representation, for space saving.
#[derive(Serialize, Deserialize)]
struct DiskNode<K> {
    node_type: NodeType,
    keys: Vec<K>,
    children: Vec<u64>,
}

struct Node<K> {
    node_type: NodeType,
    keys: Vec<K>,
    children: Vec<NodeRef<K>>,
    modified: bool,
}

impl<K: serde::de::DeserializeOwned> From<DiskNode<K>> for Node<K> {
    fn from(obj: DiskNode<K>) -> Node<K> {
        Node {
            node_type: obj.node_type,
            keys: obj.keys,
            children: obj.children.into_iter().map(|x| (NodeRef::from_offset(x))).collect(),
            modified: false,
        }
    }
}

impl<K: serde::de::DeserializeOwned> DiskNode<K> {
    fn load<R: Read+Seek>(reader: &mut R, offset: u64) -> Result<DiskNode<K>, DecodingError> {
        decode(reader, offset)
    }
}

fn load<K: serde::de::DeserializeOwned, R: Read+Seek>(reader: &mut R, offset: u64) -> Result<Node<K>, DecodingError> {
    Ok(DiskNode::<K>::load(reader, offset)?.into())
}

impl<K: serde::de::DeserializeOwned> NodeRef<K> {
    fn from_offset(offset: u64) -> NodeRef<K> {
        NodeRef(cell::UnsafeCell::new(NodeRefInternal::Unloaded(offset)))
    }

    fn from_boxed_node(node: Box<Node<K>>) -> NodeRef<K> {
        NodeRef(cell::UnsafeCell::new(NodeRefInternal::Loaded(node)))
    }

    fn load<R: Read+Seek>(&self, reader: &mut R) -> Result<(), DecodingError> {
        let internal = self.0.get();
        unsafe {
            if let NodeRefInternal::Unloaded(offset) = *internal {
                *internal = NodeRefInternal::Loaded(Box::new(load(reader, offset)?));
            }
        }
        Ok(())
    }

    fn get<R: Read+Seek>(&self, reader: &mut R) -> Result<&Node<K>, DecodingError> {
        self.load(reader)?;
        unsafe {
            Ok(match *self.0.get() {
                NodeRefInternal::Loaded(ref n) => n,
                _ => panic!("Nodes should be loaded."),
            })
        }
    }

    fn get_mut<R: Read+Seek>(&mut self, reader: &mut R) -> Result<&mut Node<K>, DecodingError> {
        self.load(reader)?;
        unsafe {
            Ok(match *self.0.get() {
                NodeRefInternal::Loaded(ref mut n) => {
                    n.modified = true;
                    n
                },
                _ => panic!("Node not loaded."),
            })
        }
    }

    fn offset_or_panic(&self, msg: &'static str) -> u64 {
        let internal = self.0.get();
        unsafe {
            match *internal {
                NodeRefInternal::Unloaded(offset) => offset,
                _ => panic!(msg),
            }
        }
    }
}

impl<K> Drop for NodeRef<K> {
    fn drop(&mut self) {
        unsafe { std::ptr::drop_in_place(self.0.get()); }
    }
}

impl<K: serde::de::DeserializeOwned+Eq+Ord+Clone> Node<K> {
    fn find_offset_for<R: Read+Seek>(&self, reader: &mut R, key: &K) -> Result<Option<u64>, DecodingError> {
        if self.node_type == NodeType::Leaf {
            if self.children.len() == 0 { return Ok(None) } //empty tree.
            match self.keys.binary_search(key) {
                Ok(ind) => Ok(Some(self.children[ind].offset_or_panic("This is a leaf, but somehow has a loaded child."))),
                Err(_) => Ok(None),
            }
        }
        else {
            self.children[self.index_of(key)].get(reader)?.find_offset_for(reader, key)
        }
    }

    fn index_of(&self, key: &K) -> usize{
        assert!(self.node_type != NodeType::Leaf);
        let ind = self.keys.binary_search(key);
        // Explanation: this returns the index of the child to the right of the last key smaller than key.
        // In this implementation, before is <= and after is >.
        // Before is the index of the key, after is the index of the key+1.
        // The not found case of binary_search is the key+1.
        match ind {
            Ok(index) | Err(index) => index,
        }
    }

    /// Modify this node in place to split in half, returning the upper half and the dividing key.
    fn split_in_place(&mut self) -> (K, Box<Node<K>>) {
        // Doing this based off keys is important.
        let half = self.keys.len()/2;
        let upper_keys = self.keys.drain(half..).collect::<Vec<_>>();
        let upper_children = self.children.drain(half..).collect::<Vec<_>>();
        assert!(self.children.len() > 1);
        assert!(self.keys.len() > 1);
        assert!(upper_children.len() > 1);
        assert!(upper_keys.len() > 1);
        let ret_key;
        let ret_node;
        match self.node_type {
            NodeType::Leaf => {
                ret_key = self.keys.last().unwrap().clone();
                ret_node = Node {
                    keys: upper_keys,
                    children: upper_children,
                    node_type: NodeType::Leaf,
                    modified: true,
                };
            }
            NodeType::Root | NodeType::Internal => {
                // A split of the root makes us an internal, and something else will construct the new root.
                self.node_type = NodeType::Internal;
                // We have one extra key in ourself right now. This is greater than any value beneath us.
                // In this implementation we go left for <=.
                ret_key = self.keys.pop().unwrap();
                ret_node = Node {
                    node_type: NodeType::Internal,
                    keys: upper_keys,
                    children: upper_children,
                    modified: true,
                }
            }
        }
        (ret_key, Box::new(ret_node))
    }

    fn insert_nonroot<R: Read+Seek>(&mut self, reader: &mut R, key: &K, value: u64, split_threshold: usize)
        -> Result<Option<(K, Box<Node<K>>)>, DecodingError>
    {
        assert!(self.node_type != NodeType::Root);
        let target = self.index_of(key);
        if self.node_type == NodeType::Leaf {
            if &self.keys[target] == key {
                self.children[target] = NodeRef::from_offset(value);
            }
            else {
                self.keys.insert(target, key.clone());
                self.children.insert(target, NodeRef::from_offset(value));
            }
        }
        else {
            let needs_split = self.children[target].get_mut(reader)?.insert_nonroot(reader, key, value, split_threshold)?;
            if let Some((k, n)) = needs_split {
                // This makes the new key "our" new maximum.
                self.keys.insert(target, k);
                // The new node is between the new key and the one after it; note the +1.
                // This works because no node is permitted to have less than 2 children.
                self.children.insert(target+1, NodeRef::from_boxed_node(n));
            }
        }
        if self.children.len() > split_threshold {
            Ok(Some(self.split_in_place()))
        }
        else { Ok(None) }
    }
}

struct BPTree<'a, R: 'a, K, V> {
    root_reference: NodeRef<K>,
    backing_io: &'a mut R,
    _phantom: PhantomData<V>
}

impl<'a, R: Read+Seek, K: serde::de::DeserializeOwned+Eq+Ord+Clone, V> BPTree<'a, R, K, V> {
    pub fn contains(&mut self, key: &K) -> Result<bool, DecodingError> {
        Ok(self.offset_for(key)?.is_some())
    }

    pub fn offset_for(&mut self, key: &K) -> Result<Option<u64>, DecodingError> {
        self.root_reference.get(self.backing_io)?.find_offset_for(self.backing_io, key)
    }
}
<commit_msg>Complete insertion.<commit_after>#![allow(dead_code)]
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate bincode;
extern crate take_mut;

use std::io::{self, Read, Seek, Write};
use std::error::Error;
use std::marker::PhantomData;
use std::cell;

#[derive(Debug)]
pub enum DecodingError {
    Corrupt(String),
    IoError(io::Error),
}

fn decode<O: serde::de::DeserializeOwned, R: Read+Seek>(reader: &mut R, offset: u64) -> Result<O, DecodingError> {
    reader.seek(io::SeekFrom::Start(offset)).map_err(|x| DecodingError::IoError(x))?;
    bincode::deserialize_from(    reader, bincode::Infinite)
    .map_err(|x| {
        match *x {
            bincode::ErrorKind::IoError(y) => DecodingError::IoError(y),
            _ => DecodingError::Corrupt(x.description().to_string()),
        }
    })
}

#[derive(Debug)]
pub enum EncodingError {
    Unknown(String),
    IoError(io::Error),
}

fn encode<O: serde::Serialize, W: Write+Seek>(writer: &mut W, obj: &O) -> Result<u64, EncodingError> {
    let offset = writer.seek(io::SeekFrom::End(0)).map_err(|x| EncodingError::IoError(x))?;
    bincode::serialize_into(writer, obj, bincode::Infinite)
    .map_err(|x| {
        match *x {
            bincode::ErrorKind::IoError(y) => EncodingError::IoError(y),
            _ => EncodingError::Unknown(x.description().to_string()),
        }
    }).map(|_| offset)
}

#[derive(Serialize, Deserialize, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
enum NodeType {
    Root,
    Internal,
    Leaf
}

enum NodeRefInternal<K> {
    Unloaded(u64),
    Loaded(Box<Node<K>>),
}

struct NodeRef<K>(cell::UnsafeCell<NodeRefInternal<K>>);

// An on-disk representation, for space saving.
#[derive(Serialize, Deserialize)]
struct DiskNode<K> {
    node_type: NodeType,
    keys: Vec<K>,
    children: Vec<u64>,
}

struct Node<K> {
    node_type: NodeType,
    keys: Vec<K>,
    children: Vec<NodeRef<K>>,
    modified: bool,
}

impl<K: serde::de::DeserializeOwned> From<DiskNode<K>> for Node<K> {
    fn from(obj: DiskNode<K>) -> Node<K> {
        Node {
            node_type: obj.node_type,
            keys: obj.keys,
            children: obj.children.into_iter().map(|x| (NodeRef::from_offset(x))).collect(),
            modified: false,
        }
    }
}

impl<K: serde::de::DeserializeOwned> DiskNode<K> {
    fn load<R: Read+Seek>(reader: &mut R, offset: u64) -> Result<DiskNode<K>, DecodingError> {
        decode(reader, offset)
    }
}

fn load<K: serde::de::DeserializeOwned, R: Read+Seek>(reader: &mut R, offset: u64) -> Result<Node<K>, DecodingError> {
    Ok(DiskNode::<K>::load(reader, offset)?.into())
}

impl<K: serde::de::DeserializeOwned> NodeRef<K> {
    fn from_offset(offset: u64) -> NodeRef<K> {
        NodeRef(cell::UnsafeCell::new(NodeRefInternal::Unloaded(offset)))
    }

    fn from_boxed_node(node: Box<Node<K>>) -> NodeRef<K> {
        NodeRef(cell::UnsafeCell::new(NodeRefInternal::Loaded(node)))
    }

    fn load<R: Read+Seek>(&self, reader: &mut R) -> Result<(), DecodingError> {
        let internal = self.0.get();
        unsafe {
            if let NodeRefInternal::Unloaded(offset) = *internal {
                *internal = NodeRefInternal::Loaded(Box::new(load(reader, offset)?));
            }
        }
        Ok(())
    }

    fn get<R: Read+Seek>(&self, reader: &mut R) -> Result<&Node<K>, DecodingError> {
        self.load(reader)?;
        unsafe {
            Ok(match *self.0.get() {
                NodeRefInternal::Loaded(ref n) => n,
                _ => panic!("Nodes should be loaded."),
            })
        }
    }

    fn get_mut<R: Read+Seek>(&mut self, reader: &mut R) -> Result<&mut Node<K>, DecodingError> {
        self.load(reader)?;
        unsafe {
            Ok(match *self.0.get() {
                NodeRefInternal::Loaded(ref mut n) => {
                    n.modified = true;
                    n
                },
                _ => panic!("Node not loaded."),
            })
        }
    }

    fn offset_or_panic(&self, msg: &'static str) -> u64 {
        let internal = self.0.get();
        unsafe {
            match *internal {
                NodeRefInternal::Unloaded(offset) => offset,
                _ => panic!(msg),
            }
        }
    }

    fn into_box<R: Read+Seek>(self, reader: &mut R) -> Result<Box<Node<K>>, DecodingError> {
        self.load(reader)?;
        let old = unsafe {
            std::mem::replace(&mut *self.0.get(), NodeRefInternal::Unloaded(0))
        };
        match old {
            NodeRefInternal::Loaded(n) => Ok(n),
            _ => panic!("Somehow, this is an unloaded node."),
        }
    }
}

impl<K> Drop for NodeRef<K> {
    fn drop(&mut self) {
        unsafe { std::ptr::drop_in_place(self.0.get()); }
    }
}

impl<K: serde::de::DeserializeOwned+Eq+Ord+Clone> Node<K> {
    fn find_offset_for<R: Read+Seek>(&self, reader: &mut R, key: &K) -> Result<Option<u64>, DecodingError> {
        if self.node_type == NodeType::Leaf {
            if self.children.len() == 0 { return Ok(None) } //empty tree.
            match self.keys.binary_search(key) {
                Ok(ind) => Ok(Some(self.children[ind].offset_or_panic("This is a leaf, but somehow has a loaded child."))),
                Err(_) => Ok(None),
            }
        }
        else {
            self.children[self.index_of(key)].get(reader)?.find_offset_for(reader, key)
        }
    }

    fn index_of(&self, key: &K) -> usize{
        assert!(self.node_type != NodeType::Leaf);
        let ind = self.keys.binary_search(key);
        // Explanation: this returns the index of the child to the right of the last key smaller than key.
        // In this implementation, before is <= and after is >.
        // Before is the index of the key, after is the index of the key+1.
        // The not found case of binary_search is the key+1.
        match ind {
            Ok(index) | Err(index) => index,
        }
    }

    /// Modify this node in place to split in half, returning the upper half and the dividing key.
    fn split_in_place(&mut self) -> (K, Box<Node<K>>) {
        // Doing this based off keys is important.
        let half = self.keys.len()/2;
        let upper_keys = self.keys.drain(half..).collect::<Vec<_>>();
        let upper_children = self.children.drain(half..).collect::<Vec<_>>();
        assert!(self.children.len() > 1);
        assert!(self.keys.len() > 1);
        assert!(upper_children.len() > 1);
        assert!(upper_keys.len() > 1);
        let ret_key;
        let ret_node;
        match self.node_type {
            NodeType::Leaf => {
                ret_key = self.keys.last().unwrap().clone();
                ret_node = Node {
                    keys: upper_keys,
                    children: upper_children,
                    node_type: NodeType::Leaf,
                    modified: true,
                };
            }
            NodeType::Root | NodeType::Internal => {
                // A split of the root makes us an internal, and something else will construct the new root.
                self.node_type = NodeType::Internal;
                // We have one extra key in ourself right now. This is greater than any value beneath us.
                // In this implementation we go left for <=.
                ret_key = self.keys.pop().unwrap();
                ret_node = Node {
                    node_type: NodeType::Internal,
                    keys: upper_keys,
                    children: upper_children,
                    modified: true,
                }
            }
        }
        (ret_key, Box::new(ret_node))
    }

    fn insert_nonroot<R: Read+Seek>(&mut self, reader: &mut R, key: &K, value: u64, split_threshold: usize)
        -> Result<Option<(K, Box<Node<K>>)>, DecodingError>
    {
        assert!(self.node_type != NodeType::Root);
        let target = self.index_of(key);
        if self.node_type == NodeType::Leaf {
            if &self.keys[target] == key {
                self.children[target] = NodeRef::from_offset(value);
            }
            else {
                self.keys.insert(target, key.clone());
                self.children.insert(target, NodeRef::from_offset(value));
            }
        }
        else {
            let needs_split = self.children[target].get_mut(reader)?.insert_nonroot(reader, key, value, split_threshold)?;
            if let Some((k, n)) = needs_split {
                // This makes the new key "our" new maximum.
                self.keys.insert(target, k);
                // The new node is between the new key and the one after it; note the +1.
                // This works because no node is permitted to have less than 2 children.
                self.children.insert(target+1, NodeRef::from_boxed_node(n));
            }
        }
        if self.children.len() > split_threshold {
            Ok(Some(self.split_in_place()))
        }
        else { Ok(None) }
    }

    /// If the root splits, sets our type to internal and/or leaf depending, then returns the new sibling.
    fn insert<R: Read+Seek>(&mut self, reader: &mut R, key: &K, value: u64, order: u64) -> Result<Option<(K, Box<Node<K>>)>, DecodingError> {
        let split_threshold = (order/2+order%2) as usize;
        let target = self.index_of(key);
        let needs_split = self.children[target].get_mut(reader)?.insert_nonroot(reader, key, value, split_threshold)?;
        if let Some((k, n)) = needs_split {
            // Same as insert_nonroot.
            self.keys.insert(target, k);
            self.children.insert(target+1, NodeRef::from_boxed_node(n));
        }
        if self.children.len() > split_threshold {
            let new_type = match self.node_type {
                NodeType::Leaf => NodeType::Leaf,
                NodeType::Root => NodeType::Internal,
                _ => panic!("Should be a root or a lweaf."),
            };
            self.node_type = new_type;
            Ok(Some(self.split_in_place()))
        }
        else { Ok(None) }
    }
}

struct BPOffsetTree<'a, R: 'a, K> {
    root_reference: NodeRef<K>,
    backing_io: &'a mut R,
    order: u64,
}

impl<'a, R: Read+Seek, K: serde::de::DeserializeOwned+Eq+Ord+Clone> BPOffsetTree<'a, R, K> {
    fn contains(&mut self, key: &K) -> Result<bool, DecodingError> {
        Ok(self.offset_for(key)?.is_some())
    }

    fn offset_for(&mut self, key: &K) -> Result<Option<u64>, DecodingError> {
        self.root_reference.get(self.backing_io)?.find_offset_for(self.backing_io, key)
    }

    fn insert(&mut self, key: &K, value: u64) -> Result<(), DecodingError> {
        let needs_split = self.root_reference.get_mut(self.backing_io)?.insert(&mut self.backing_io, key, value, self.order)?;
        if let Some((k, right)) = needs_split {
            // This is a hack to get around moving out.
            let r = std::mem::replace(&mut self.root_reference, NodeRef::from_offset(0));
            let left = r.into_box(self.backing_io)?;
            let new_node = Node {
                node_type: NodeType::Root,
                modified: true,
                keys: vec![k],
                children: vec![NodeRef::from_boxed_node(left), NodeRef::from_boxed_node(right)],
            };
            self.root_reference = NodeRef::from_boxed_node(Box::new(new_node));
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// DOCS

extern crate tempdir;

use std::error::Error as ErrorTrait;
use std::fmt;
use std::io;
use std::fs;
use std::borrow::Borrow;
use std::path;
use std::convert::AsRef;

use tempdir::TempDir;

pub use OverwriteBehavior::{AllowOverwrite, DisallowOverwrite};

#[derive(Clone,Copy)]
pub enum OverwriteBehavior {
    /// Overwrite files silently.
    AllowOverwrite,

    /// Don't overwrite files. `AtomicFile.write` will raise errors for such conditions only after
    /// you've already written your data.
    DisallowOverwrite
}

/// Represents an error raised by `AtomicFile.write`.
#[derive(Debug)]
pub enum Error<E> {
    /// The error originated in the library itself, while it was either creating a temporary file
    /// or moving the file into place.
    Internal(io::Error),
    /// The error originated in the user-supplied callback.
    User(E)
}

impl<E: fmt::Display> fmt::Display for Error<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Internal(ref e) => e.fmt(f),
            Error::User(ref e) => e.fmt(f)
        }
    }
}

impl<E: ErrorTrait> ErrorTrait for Error<E> {
    fn description(&self) -> &str {
        match *self {
            Error::Internal(ref e) => e.description(),
            Error::User(ref e) => e.description()
        }
    }

    fn cause(&self) -> Option<&ErrorTrait> {
        match *self {
            Error::Internal(ref e) => Some(e),
            Error::User(ref e) => Some(e)
        }
    }
}

pub struct AtomicFile {
    path: path::PathBuf,
    overwrite: OverwriteBehavior,
    tmpdir: path::PathBuf
}

impl AtomicFile {
    /// Helper for writing to `path` in write-only mode.
    ///
    /// If `DisallowOverwrite` is given, errors will be returned from `self.write(...)` if the file
    /// exists.
    pub fn new<P: AsRef<path::Path>>(path: P, overwrite: OverwriteBehavior) -> Self {
        let p = path.as_ref();
        AtomicFile::new_with_tmpdir(p, overwrite, p.parent().unwrap_or(p))
    }

    pub fn new_with_tmpdir<P: AsRef<path::Path>>(path: P, overwrite: OverwriteBehavior, tmpdir: P) -> Self {
        AtomicFile {
            path: path.as_ref().to_path_buf(),
            overwrite: overwrite,
            tmpdir: tmpdir.as_ref().to_path_buf()
        }
    }

    fn commit(&self, tmppath: &path::Path) -> io::Result<()> {
        match self.overwrite {
            AllowOverwrite => replace_atomic(tmppath, self.path()),
            DisallowOverwrite => move_atomic(tmppath, self.path())
        }
    }

    /// Get the target filepath.
    pub fn path(&self) -> &path::Path { &self.path.borrow() }

    /// Open a temporary file, call `f` on it (which is supposed to write to it), then move the
    /// file atomically to `self.path`.
    pub fn write<T, E, F>(&self, f: F) -> Result<T, Error<E>> where
        F: FnOnce(&mut fs::File) -> Result<T, E>
    {
        macro_rules! try_internal {
            ($expr:expr) => {
                match $expr {
                    Ok(r) => r,
                    Err(e) => return Err(Error::Internal(e))
                }
            };
        }

        let tmpdir = try_internal!(TempDir::new_in(
            &self.tmpdir,
            ".atomicwrite"
        ));

        let tmppath = tmpdir.path().join("tmpfile.tmp");
        let rv = {
            let mut tmpfile = try_internal!(fs::File::create(&tmppath));
            try!(f(&mut tmpfile).map_err(Error::User))
        };
        try_internal!(self.commit(&tmppath));
        Ok(rv)
    }
}

#[cfg(unix)]
mod imp {
    extern crate nix;

    use std::{io,fs,path};
    use std::os::unix::io::AsRawFd;

    fn fsync_dir(x: &path::Path) -> io::Result<()> {
        let f = try!(fs::File::open(x));
        try!(nix::unistd::fsync(f.as_raw_fd()));
        Ok(())
    }

    pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        try!(fs::rename(src, dst));

        let dst_directory = dst.parent().unwrap();
        try!(fsync_dir(dst_directory));
        Ok(())
    }

    pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        try!(fs::hard_link(src, dst));
        try!(fs::remove_file(src));

        let src_directory = src.parent().unwrap();
        let dst_directory = dst.parent().unwrap();
        try!(fsync_dir(dst_directory));
        if src_directory != dst_directory { try!(fsync_dir(src_directory)); }
        Ok(())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;
    extern crate kernel32 as win32kernel;

    use std::{io,path};
    use std::ffi::OsStr;
    use std::os::windows::ffi::OsStrExt;

    macro_rules! call {
        ($e: expr) => (
            if $e != 0 {
                Ok(())
            } else {
                Err(io::Error::last_os_error())
            }
        )
    }

    fn path_to_windows_str<T: AsRef<OsStr>>(x: T) -> Vec<winapi::WCHAR> {
        x.as_ref().encode_wide().chain(Some(0)).collect()
    }

    pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        call!(unsafe {win32kernel::MoveFileExW(
            path_to_windows_str(src).as_ptr(), path_to_windows_str(dst).as_ptr(),
            winapi::MOVEFILE_WRITE_THROUGH | winapi::MOVEFILE_REPLACE_EXISTING
        )})
    }

    pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        call!(unsafe {win32kernel::MoveFileExW(
            path_to_windows_str(src).as_ptr(), path_to_windows_str(dst).as_ptr(),
            winapi::MOVEFILE_WRITE_THROUGH
        )})
    }
}

/// Move `src` to `dst`. If `dst` exists, it will be silently overwritten.
///
/// Both paths must reside on the same filesystem for the operation to be atomic.
pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
    imp::replace_atomic(src, dst)
}

/// Move `src` to `dst`. An error will be returned if `dst` exists.
///
/// Both paths must reside on the same filesystem for the operation to be atomic.
pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
    imp::move_atomic(src, dst)
}
<commit_msg>Remove unnecessary helper macro<commit_after>// DOCS

extern crate tempdir;

use std::error::Error as ErrorTrait;
use std::fmt;
use std::io;
use std::fs;
use std::borrow::Borrow;
use std::path;
use std::convert::AsRef;

use tempdir::TempDir;

pub use OverwriteBehavior::{AllowOverwrite, DisallowOverwrite};

#[derive(Clone,Copy)]
pub enum OverwriteBehavior {
    /// Overwrite files silently.
    AllowOverwrite,

    /// Don't overwrite files. `AtomicFile.write` will raise errors for such conditions only after
    /// you've already written your data.
    DisallowOverwrite
}

/// Represents an error raised by `AtomicFile.write`.
#[derive(Debug)]
pub enum Error<E> {
    /// The error originated in the library itself, while it was either creating a temporary file
    /// or moving the file into place.
    Internal(io::Error),
    /// The error originated in the user-supplied callback.
    User(E)
}

impl<E: fmt::Display> fmt::Display for Error<E> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Internal(ref e) => e.fmt(f),
            Error::User(ref e) => e.fmt(f)
        }
    }
}

impl<E: ErrorTrait> ErrorTrait for Error<E> {
    fn description(&self) -> &str {
        match *self {
            Error::Internal(ref e) => e.description(),
            Error::User(ref e) => e.description()
        }
    }

    fn cause(&self) -> Option<&ErrorTrait> {
        match *self {
            Error::Internal(ref e) => Some(e),
            Error::User(ref e) => Some(e)
        }
    }
}

pub struct AtomicFile {
    path: path::PathBuf,
    overwrite: OverwriteBehavior,
    tmpdir: path::PathBuf
}

impl AtomicFile {
    /// Helper for writing to `path` in write-only mode.
    ///
    /// If `DisallowOverwrite` is given, errors will be returned from `self.write(...)` if the file
    /// exists.
    pub fn new<P: AsRef<path::Path>>(path: P, overwrite: OverwriteBehavior) -> Self {
        let p = path.as_ref();
        AtomicFile::new_with_tmpdir(p, overwrite, p.parent().unwrap_or(p))
    }

    pub fn new_with_tmpdir<P: AsRef<path::Path>>(path: P, overwrite: OverwriteBehavior, tmpdir: P) -> Self {
        AtomicFile {
            path: path.as_ref().to_path_buf(),
            overwrite: overwrite,
            tmpdir: tmpdir.as_ref().to_path_buf()
        }
    }

    fn commit(&self, tmppath: &path::Path) -> io::Result<()> {
        match self.overwrite {
            AllowOverwrite => replace_atomic(tmppath, self.path()),
            DisallowOverwrite => move_atomic(tmppath, self.path())
        }
    }

    /// Get the target filepath.
    pub fn path(&self) -> &path::Path { &self.path.borrow() }

    /// Open a temporary file, call `f` on it (which is supposed to write to it), then move the
    /// file atomically to `self.path`.
    pub fn write<T, E, F>(&self, f: F) -> Result<T, Error<E>> where
        F: FnOnce(&mut fs::File) -> Result<T, E>
    {
        let tmpdir = try!(TempDir::new_in(
            &self.tmpdir,
            ".atomicwrite"
        ).map_err(Error::Internal));

        let tmppath = tmpdir.path().join("tmpfile.tmp");
        let rv = {
            let mut tmpfile = try!(fs::File::create(&tmppath).map_err(Error::Internal));
            try!(f(&mut tmpfile).map_err(Error::User))
        };
        try!(self.commit(&tmppath).map_err(Error::Internal));
        Ok(rv)
    }
}

#[cfg(unix)]
mod imp {
    extern crate nix;

    use std::{io,fs,path};
    use std::os::unix::io::AsRawFd;

    fn fsync_dir(x: &path::Path) -> io::Result<()> {
        let f = try!(fs::File::open(x));
        try!(nix::unistd::fsync(f.as_raw_fd()));
        Ok(())
    }

    pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        try!(fs::rename(src, dst));

        let dst_directory = dst.parent().unwrap();
        try!(fsync_dir(dst_directory));
        Ok(())
    }

    pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        try!(fs::hard_link(src, dst));
        try!(fs::remove_file(src));

        let src_directory = src.parent().unwrap();
        let dst_directory = dst.parent().unwrap();
        try!(fsync_dir(dst_directory));
        if src_directory != dst_directory { try!(fsync_dir(src_directory)); }
        Ok(())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;
    extern crate kernel32 as win32kernel;

    use std::{io,path};
    use std::ffi::OsStr;
    use std::os::windows::ffi::OsStrExt;

    macro_rules! call {
        ($e: expr) => (
            if $e != 0 {
                Ok(())
            } else {
                Err(io::Error::last_os_error())
            }
        )
    }

    fn path_to_windows_str<T: AsRef<OsStr>>(x: T) -> Vec<winapi::WCHAR> {
        x.as_ref().encode_wide().chain(Some(0)).collect()
    }

    pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        call!(unsafe {win32kernel::MoveFileExW(
            path_to_windows_str(src).as_ptr(), path_to_windows_str(dst).as_ptr(),
            winapi::MOVEFILE_WRITE_THROUGH | winapi::MOVEFILE_REPLACE_EXISTING
        )})
    }

    pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
        call!(unsafe {win32kernel::MoveFileExW(
            path_to_windows_str(src).as_ptr(), path_to_windows_str(dst).as_ptr(),
            winapi::MOVEFILE_WRITE_THROUGH
        )})
    }
}

/// Move `src` to `dst`. If `dst` exists, it will be silently overwritten.
///
/// Both paths must reside on the same filesystem for the operation to be atomic.
pub fn replace_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
    imp::replace_atomic(src, dst)
}

/// Move `src` to `dst`. An error will be returned if `dst` exists.
///
/// Both paths must reside on the same filesystem for the operation to be atomic.
pub fn move_atomic(src: &path::Path, dst: &path::Path) -> io::Result<()> {
    imp::move_atomic(src, dst)
}
<|endoftext|>


<commit_before>
#![feature(unsafe_destructor)]
#[allow(dead_code)]

extern crate libc;
use std::rc::{Rc};
use std::cell::{RefCell};
use std::collections::{HashMap};
use mruby::{mrb_open,mrb_close,mrb_state,mrb_value,Struct_RClass};
mod mruby;

pub type MrbState = Rc<RefCell<*mut mrb_state>>;
pub type MrbClass = Rc<RefCell<*mut Struct_RClass>>;

pub struct Mrb {
    mrb : MrbState,
    clzs : RefCell<HashMap<String,MrbClass>>
}

#[deriving(PartialEq,Clone)]
pub struct Class {
    mrb : MrbState,
    clz : MrbClass
}

impl Mrb {

    pub fn new() -> Mrb {
        let mrb = unsafe {
            mrb_open()
        };
        let state = unsafe { *mrb };
        let clzs : RefCell<HashMap<String,MrbClass>> = RefCell::new(HashMap::new());
        let m = Mrb{ mrb:Rc::new(RefCell::new(mrb)),clzs:clzs };
        m.clzs.borrow_mut().insert(String::from_str("Object"),Rc::new(RefCell::new(state.object_class)));
        m.clzs.borrow_mut().insert(String::from_str("Class"),Rc::new(RefCell::new(state.class_class)));
        m.clzs.borrow_mut().insert(String::from_str("String"),Rc::new(RefCell::new(state.string_class)));
        m.clzs.borrow_mut().insert(String::from_str("Array"),Rc::new(RefCell::new(state.array_class)));
        m.clzs.borrow_mut().insert(String::from_str("Hash"),Rc::new(RefCell::new(state.hash_class)));
        m
    }

    pub fn close(&self) {
        if std::rc::is_unique(&self.mrb) {
            let state = self.get_mrb();
            unsafe {
                mrb_close(state);
            }
        }
    }

    #[inline(always)]
    fn get_mrb(&self) -> *mut mrb_state {
        *(self.mrb).borrow_mut()
    }

    pub fn get_class(&self,name:&str) -> Option<Class> {
        let mut h = self.clzs.borrow_mut();
        let clzname = String::from_str(name);
        if h.get(&clzname).is_some() {
                return Some(Class{mrb:self.mrb.clone(),clz:h.get(&clzname).unwrap().clone()})
        }
        let mclz = unsafe {
            mruby::mrb_class_get(self.get_mrb(),
                                 name.as_ptr() as *const libc::c_char)
        };
        if mclz.is_null() {
            None
        } else {
            let class = Class{mrb:self.mrb.clone(),clz:Rc::new(RefCell::new(mclz))};
            h.insert(clzname,class.clz.clone());
            Some(class)
        }
    }

    pub fn load(&self,code:&str) -> mrb_value {
        unsafe {
            let state = self.get_mrb();
            let mut sjmp = (*state).jmp;
            if sjmp as uint != 0 {
                panic!("jmp not null");
            }
            let jmp = libc::malloc(std::mem::size_of::<mruby::jmp_buf>() as libc::size_t) as *mut mruby::jmp_buf;
            if mruby::setjmp(*jmp) as int == 0 {
                sjmp = jmp;
                let val = mruby::mrb_load_string(state,code.as_ptr() as *const libc::c_char);
                sjmp = 0 as *mut _;
                return val;
            } else {
                mruby::mrb_print_error(state);
                panic!("fail to setjmp");
            }
        }
    }

    pub fn def_class(&self,name:&str,outer:&str) -> Option<Class> {
        if self.get_class(name).is_some() {
            panic!("class already have");
        }
        match self.get_class(outer) {
            Some(out_clz) => {
                let clz = unsafe {
                    mruby::mrb_define_class(self.get_mrb(),
                                 name.as_ptr() as *const libc::c_char,
                                 out_clz.get_clz()
                                 )
                };
                let class = Class{mrb:self.mrb.clone(),clz:Rc::new(RefCell::new(clz))};
                let mut h = self.clzs.borrow_mut();
                h.insert(String::from_str(name),class.clz.clone());
                Some(class)
            },
            None => {
                None
            }
        }
    }

}

impl Class {
    #[inline(always)]
    fn get_clz(&self) -> *mut Struct_RClass {
        *(self.clz).borrow_mut()
    }
}
/*
#[unsafe_destructor]
impl Drop for RefCell<*mut mrb_state> {
    fn drop(&mut self) {
        let mrb = *self.borrow_mut();
        if !mrb.is_null() {
            println!("mrb pointer null");
            unsafe {
                mrb_close(mrb);
            }
        }
    }
}
*/

#[test]
fn test_mrb_open() {
    let m = Mrb::new();
    m.close();
}

#[test]
fn test_get_class() {
    let m = Mrb::new();
    let arr_clz = m.get_class("Array").unwrap();
    assert!(m.get_class("Class").is_some());
    assert!(m.get_class("Object").is_some());
    assert!(m.get_class("Hash").is_some());
    assert!(m.get_class("String").is_some());
    m.close();
    assert!(m.get_class("String").is_some());
    assert!(std::rc::strong_count(&arr_clz.mrb) == 2);
}

#[test]
fn test_load_str() {
    let m = Mrb::new();
    let mut v = m.load("1..3.each do |i| puts i end");
    assert!(v.is_nil());
    m.close();
}

#[test]
fn test_def_class() {
    let m = Mrb::new();
    let hello = m.def_class("Hello","Object");
    assert!(hello.is_some());
    m.close();
}
<commit_msg>add call & obj_new<commit_after>
#![feature(unsafe_destructor)]
#[allow(dead_code)]

extern crate libc;
use std::rc::{Rc};
use std::cell::{RefCell};
use std::collections::{HashMap};
use mruby::{mrb_open,mrb_close,mrb_state,mrb_value,Struct_RClass,mrb_int};
mod mruby;

pub type MrbState = Rc<RefCell<*mut mrb_state>>;
pub type MrbClass = Rc<RefCell<*mut Struct_RClass>>;

pub struct Mrb {
    mrb : MrbState,
    clzs : RefCell<HashMap<String,MrbClass>>
}

#[deriving(PartialEq,Clone)]
pub struct Class {
    mrb : MrbState,
    clz : MrbClass
}

impl Mrb {

    pub fn new() -> Mrb {
        let mrb = unsafe {
            mrb_open()
        };
        let state = unsafe { *mrb };
        let clzs : RefCell<HashMap<String,MrbClass>> = RefCell::new(HashMap::new());
        let m = Mrb{ mrb:Rc::new(RefCell::new(mrb)),clzs:clzs };
        m.clzs.borrow_mut().insert(String::from_str("Object"),Rc::new(RefCell::new(state.object_class)));
        m.clzs.borrow_mut().insert(String::from_str("Class"),Rc::new(RefCell::new(state.class_class)));
        m.clzs.borrow_mut().insert(String::from_str("String"),Rc::new(RefCell::new(state.string_class)));
        m.clzs.borrow_mut().insert(String::from_str("Array"),Rc::new(RefCell::new(state.array_class)));
        m.clzs.borrow_mut().insert(String::from_str("Hash"),Rc::new(RefCell::new(state.hash_class)));
        m
    }

    pub fn close(&self) {
        if std::rc::is_unique(&self.mrb) {
            let state = self.get_mrb();
            unsafe {
                mrb_close(state);
            }
        }
    }

    #[inline(always)]
    fn get_mrb(&self) -> *mut mrb_state {
        *(self.mrb).borrow_mut()
    }

    pub fn get_class(&self,name:&str) -> Option<Class> {
        let mut h = self.clzs.borrow_mut();
        let clzname = String::from_str(name);
        if h.get(&clzname).is_some() {
                return Some(Class{mrb:self.mrb.clone(),clz:h.get(&clzname).unwrap().clone()})
        }
        let mclz = unsafe {
            mruby::mrb_class_get(self.get_mrb(),
                                 name.as_ptr() as *const libc::c_char)
        };
        if mclz.is_null() {
            None
        } else {
            let class = Class{mrb:self.mrb.clone(),clz:Rc::new(RefCell::new(mclz))};
            h.insert(clzname,class.clz.clone());
            Some(class)
        }
    }

    pub fn load(&self,code:&str) -> mrb_value {
        unsafe {
            let state = self.get_mrb();
            let mut sjmp = (*state).jmp;
            if sjmp as uint != 0 {
                panic!("jmp not null");
            }
            let jmp = libc::malloc(std::mem::size_of::<mruby::jmp_buf>() as libc::size_t) as *mut mruby::jmp_buf;
            if mruby::setjmp(*jmp) as int == 0 {
                sjmp = jmp;
                let val = mruby::mrb_load_string(state,code.as_ptr() as *const libc::c_char);
                sjmp = 0 as *mut _;
                return val;
            } else {
                mruby::mrb_print_error(state);
                panic!("fail to setjmp");
            }
            libc::free(jmp as *mut _);
        }
    }

    pub fn def_class(&self,name:&str,outer:&str) -> Option<Class> {
        if self.get_class(name).is_some() {
            panic!("class already have");
        }
        match self.get_class(outer) {
            Some(out_clz) => {
                let clz = unsafe {
                    mruby::mrb_define_class(self.get_mrb(),
                                 name.as_ptr() as *const libc::c_char,
                                 out_clz.get_clz()
                                 )
                };
                let class = Class{mrb:self.mrb.clone(),clz:Rc::new(RefCell::new(clz))};
                let mut h = self.clzs.borrow_mut();
                h.insert(String::from_str(name),class.clz.clone());
                Some(class)
            },
            None => {
                None
            }
        }
    }

    pub fn call(&self,v:&mrb_value,method:&str) {
        unsafe {
            mruby::mrb_funcall(self.get_mrb(),*v,
                                 method.as_ptr() as *const libc::c_char,0 as mrb_int);
        }
    }

    pub fn inspect(&self,v:&mrb_value) {
        self.call(v,"to_s");
    }

}

impl Class {
    #[inline(always)]
    fn get_clz(&self) -> *mut Struct_RClass {
        *(self.clz).borrow_mut()
    }

    #[inline(always)]
    fn get_mrb(&self) -> *mut mrb_state {
        *(self.mrb).borrow_mut()
    }

    fn new(&self) -> mrb_value {
        unsafe {
            mruby::mrb_obj_new(self.get_mrb(),self.get_clz(),0 as mrb_int,0 as *const _)
        }
    }
}
/*
#[unsafe_destructor]
impl Drop for RefCell<*mut mrb_state> {
    fn drop(&mut self) {
        let mrb = *self.borrow_mut();
        if !mrb.is_null() {
            println!("mrb pointer null");
            unsafe {
                mrb_close(mrb);
            }
        }
    }
}
*/

#[test]
fn test_mrb_open() {
    let m = Mrb::new();
    m.close();
}

#[test]
fn test_get_class() {
    let m = Mrb::new();
    let arr_clz = m.get_class("Array").unwrap();
    assert!(m.get_class("Class").is_some());
    assert!(m.get_class("Object").is_some());
    assert!(m.get_class("Hash").is_some());
    assert!(m.get_class("String").is_some());
    m.close();
    assert!(m.get_class("String").is_some());
    assert!(std::rc::strong_count(&arr_clz.mrb) == 2);
}

#[test]
fn test_obj_new() {
    let m = Mrb::new();
    let arr_clz = m.get_class("Array").unwrap();
    let v = arr_clz.new();
    m.inspect(&v);
    assert!(v.is_nil());
    m.close();
}

#[test]
fn test_load_str() {
    let m = Mrb::new();
    let mut v = m.load("def inc(a) a+1 end; return inc(2)");
    assert!(v.is_nil());
    m.call(&v,"to_s");
    m.close();
}

#[test]
fn test_def_class() {
    let m = Mrb::new();
    let hello = m.def_class("Hello","Object");
    assert!(hello.is_some());
    m.close();
}
<|endoftext|>


<commit_before>//! CHIP-8 emulator backend.
//! You can develop your own frontends for it.
//!
//! The reference frontend is
//! [crusty-chip-sfml](https://github.com/crumblingstatue/crusty-chip-sfml).
//!
//! The CHIP-8 technical documentation in the comments is copied from
//! http://devernay.free.fr/hacks/chip8/C8TECH10.HTM,
//! Copyright (c) Thomas P. Greene.

#![experimental]

use std::slice::bytes::copy_memory;

mod ops;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static fontset: [u8, .. 5 * 0x10] = [
    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
    0xF0, 0x80, 0xF0, 0x80, 0x80, // F
];

/// Draw callback.
///
/// Takes a slice of u8, where each u8 is a pixel.
///
/// A **non-zero** pixel is visible, a **zero** pixel is not visible.
///
/// The coordinate system is from top-left to bottom-right.
///
/// Example:
///
/// ```rust
///     use crusty_chip::{ DISPLAY_WIDTH, VirtualMachine };
///     // Dump the pixels to stdout
///     fn dump_pixels(pixels: &[u8]) {
///         for (i, px) in pixels.iter().enumerate() {
///             match *px {
///                 0 => print!(" "),
///                 _ => print!("#")
///             }
///             if i % DISPLAY_WIDTH == 0 {
///                 print!("\n");
///             }
///         }
///     }
///     let mut ch8 = VirtualMachine::new(dump_pixels);
///     // ...
/// ```
pub type DrawCallback<'a> = |pixels: &[u8]|: 'a;

struct KeypressWait {
    wait: bool,
    vx: uint
}

/// CHIP-8 virtual machine
pub struct VirtualMachine<'a> {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
    draw_callback: DrawCallback<'a>,
    keys: [bool, .. 16],
    keypress_wait: KeypressWait
}

impl <'a> VirtualMachine <'a> {

    /// Constructs a new `VirtualMachine`.
    ///
    /// ## Arguments ##
    /// * draw_callback - Callback used when drawing
    pub fn new(draw_callback: DrawCallback<'a>) -> VirtualMachine<'a> {
        let mut ch8 = VirtualMachine {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
            draw_callback: draw_callback,
            keys: [false, .. 16],
            keypress_wait: KeypressWait {
                wait: false,
                vx: 0
            }
        };
        copy_memory(ch8.ram.slice_mut(0u, 5 * 0x10), fontset);
        ch8
    }

    /// Load a ROM
    ///
    /// ## Arguments ##
    /// * rom - ROM to load
    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.slice_mut(START_ADDR as uint, len), rom);
    }

    /// Do an emulation cycle.
    pub fn do_cycle(&mut self) {
        if self.keypress_wait.wait {
            return;
        }

        let ins = self.fetch_ins();
        self.dispatch(ins);
    }

    // Decode instruction and execute it
    fn dispatch(&mut self, ins: u16) {
        // A 12-bit value, the lowest 12 bits of the instruction
        let nnn = ins & 0x0FFF;
        // A 4-bit value, the lowest 4 bits of the instruction
        let n = ins & 0x000F;
        // A 4-bit value, the lower 4 bits of the high byte of the instruction
        let x = (ins & 0x0F00) >> 8;
        // A 4-bit value, the upper 4 bits of the low byte of the instruction
        let y = (ins & 0x00F0) >> 4;
        // kk or byte - An 8-bit value, the lowest 8 bits of the instruction
        let kk = (ins & 0x00FF) as u8;

        match (ins & 0xF000) >> 12 {
            0x0 => match nnn {
                0x0E0 => ops::clear_display(self, ),
                0x0EE => ops::ret_from_subroutine(self, ),
                _ => ops::jump_to_sys_routine(self, 0)
            },
            0x1 => ops::jump_addr(self, nnn),
            0x2 => ops::call_subroutine(self, nnn as uint),
            0x3 => ops::skip_next_vx_eq(self, x as uint, kk),
            0x4 => ops::skip_next_vx_ne(self, x as uint, kk),
            0x5 => match n {
                0x0 => ops::skip_next_vx_eq_vy(self, x as uint, y as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6 => ops::set_vx_byte(self, x as uint, kk),
            0x7 => ops::add_vx_byte(self, x as uint, kk),
            0x8 => {
                let (x, y) = (x as uint, y as uint);
                match n {
                    0x0 => ops::set_vx_to_vy(self, x, y),
                    0x1 => ops::set_vx_to_vx_or_vy(self, x, y),
                    0x2 => ops::set_vx_to_vx_and_vy(self, x, y),
                    0x3 => ops::set_vx_to_vx_xor_vy(self, x, y),
                    0x4 => ops::add_vx_vy(self, x, y),
                    0x5 => ops::sub_vx_vy(self, x, y),
                    0x6 => ops::set_vx_to_vx_shr_1(self, x),
                    0xE => ops::set_vx_to_vx_shl_1(self, x),
                    _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
                }
            },
            0x9 => match n {
                0x0 => ops::skip_next_vx_ne_vy(self, x as uint, y as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA => ops::set_i(self, nnn),
            0xC => ops::set_vx_rand_and(self, x as uint, kk),
            0xD => ops::display_sprite(self, x as uint, y as uint, n as uint),
            0xE => match kk {
                0xA1 => ops::skip_next_key_vx_not_pressed(self, x as uint),
                0x9E => ops::skip_next_key_vx_pressed(self, x as uint),
                _ => fail!("Unknown 0xEXXX instruction: {:x}", ins)
            },
            0xF => {
                match kk {
                    0x07 => ops::set_vx_to_delay_timer(self, x as uint),
                    0x0A => ops::wait_for_keypress_store_in_vx(self, x as uint),
                    0x15 => ops::set_delay_timer(self, x as uint),
                    0x18 => ops::set_sound_timer(self, x as uint),
                    0x1E => ops::add_vx_to_i(self, x as uint),
                    0x29 => ops::set_i_to_loc_of_digit_vx(self, x as uint),
                    0x33 => ops::store_bcd_of_vx_to_i(self, x as uint),
                    0x55 => ops::copy_v0_through_vx_to_mem(self, x as uint),
                    0x65 => ops::read_v0_through_vx_from_mem(self, x as uint),
                    _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
                }
            },
            _ => fail!("Unknown instruction: {:04x}", ins)
        }
    }

    fn fetch_ins(&mut self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        self.pc += 2;
        b1 as u16 << 8 | b2 as u16
    }

    /// Press a key on the hexadecimal keypad
    ///
    /// `key` should be in the range `0..15`
    pub fn press_key(&mut self, key: uint) {
        assert!(key <= 15);
        self.keys[key] = true;
        if self.keypress_wait.wait {
            self.v[self.keypress_wait.vx] = key as u8;
            self.keypress_wait.wait = false;
        }
    }

    /// Release a key on the hexadecimal keypad
    ///
    /// `key` should be in the range `0..15`
    pub fn release_key(&mut self, key: uint) {
        assert!(key <= 15);
        self.keys[key] = false;
    }

    /// Decrement the sound and delay timers
    ///
    /// They should be decremented at a rate of 60 Hz
    pub fn decrement_timers(&mut self) {
        if self.delay_timer > 0 {
            self.delay_timer -= 1;
        }
        if self.sound_timer > 0 {
            self.sound_timer -= 1;
        }
    }
}
<commit_msg>Rename static fontset to FONTSET<commit_after>//! CHIP-8 emulator backend.
//! You can develop your own frontends for it.
//!
//! The reference frontend is
//! [crusty-chip-sfml](https://github.com/crumblingstatue/crusty-chip-sfml).
//!
//! The CHIP-8 technical documentation in the comments is copied from
//! http://devernay.free.fr/hacks/chip8/C8TECH10.HTM,
//! Copyright (c) Thomas P. Greene.

#![experimental]

use std::slice::bytes::copy_memory;

mod ops;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static FONTSET: [u8, .. 5 * 0x10] = [
    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
    0xF0, 0x80, 0xF0, 0x80, 0x80, // F
];

/// Draw callback.
///
/// Takes a slice of u8, where each u8 is a pixel.
///
/// A **non-zero** pixel is visible, a **zero** pixel is not visible.
///
/// The coordinate system is from top-left to bottom-right.
///
/// Example:
///
/// ```rust
///     use crusty_chip::{ DISPLAY_WIDTH, VirtualMachine };
///     // Dump the pixels to stdout
///     fn dump_pixels(pixels: &[u8]) {
///         for (i, px) in pixels.iter().enumerate() {
///             match *px {
///                 0 => print!(" "),
///                 _ => print!("#")
///             }
///             if i % DISPLAY_WIDTH == 0 {
///                 print!("\n");
///             }
///         }
///     }
///     let mut ch8 = VirtualMachine::new(dump_pixels);
///     // ...
/// ```
pub type DrawCallback<'a> = |pixels: &[u8]|: 'a;

struct KeypressWait {
    wait: bool,
    vx: uint
}

/// CHIP-8 virtual machine
pub struct VirtualMachine<'a> {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
    draw_callback: DrawCallback<'a>,
    keys: [bool, .. 16],
    keypress_wait: KeypressWait
}

impl <'a> VirtualMachine <'a> {

    /// Constructs a new `VirtualMachine`.
    ///
    /// ## Arguments ##
    /// * draw_callback - Callback used when drawing
    pub fn new(draw_callback: DrawCallback<'a>) -> VirtualMachine<'a> {
        let mut ch8 = VirtualMachine {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
            draw_callback: draw_callback,
            keys: [false, .. 16],
            keypress_wait: KeypressWait {
                wait: false,
                vx: 0
            }
        };
        copy_memory(ch8.ram.slice_mut(0u, 5 * 0x10), FONTSET);
        ch8
    }

    /// Load a ROM
    ///
    /// ## Arguments ##
    /// * rom - ROM to load
    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.slice_mut(START_ADDR as uint, len), rom);
    }

    /// Do an emulation cycle.
    pub fn do_cycle(&mut self) {
        if self.keypress_wait.wait {
            return;
        }

        let ins = self.fetch_ins();
        self.dispatch(ins);
    }

    // Decode instruction and execute it
    fn dispatch(&mut self, ins: u16) {
        // A 12-bit value, the lowest 12 bits of the instruction
        let nnn = ins & 0x0FFF;
        // A 4-bit value, the lowest 4 bits of the instruction
        let n = ins & 0x000F;
        // A 4-bit value, the lower 4 bits of the high byte of the instruction
        let x = (ins & 0x0F00) >> 8;
        // A 4-bit value, the upper 4 bits of the low byte of the instruction
        let y = (ins & 0x00F0) >> 4;
        // kk or byte - An 8-bit value, the lowest 8 bits of the instruction
        let kk = (ins & 0x00FF) as u8;

        match (ins & 0xF000) >> 12 {
            0x0 => match nnn {
                0x0E0 => ops::clear_display(self, ),
                0x0EE => ops::ret_from_subroutine(self, ),
                _ => ops::jump_to_sys_routine(self, 0)
            },
            0x1 => ops::jump_addr(self, nnn),
            0x2 => ops::call_subroutine(self, nnn as uint),
            0x3 => ops::skip_next_vx_eq(self, x as uint, kk),
            0x4 => ops::skip_next_vx_ne(self, x as uint, kk),
            0x5 => match n {
                0x0 => ops::skip_next_vx_eq_vy(self, x as uint, y as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6 => ops::set_vx_byte(self, x as uint, kk),
            0x7 => ops::add_vx_byte(self, x as uint, kk),
            0x8 => {
                let (x, y) = (x as uint, y as uint);
                match n {
                    0x0 => ops::set_vx_to_vy(self, x, y),
                    0x1 => ops::set_vx_to_vx_or_vy(self, x, y),
                    0x2 => ops::set_vx_to_vx_and_vy(self, x, y),
                    0x3 => ops::set_vx_to_vx_xor_vy(self, x, y),
                    0x4 => ops::add_vx_vy(self, x, y),
                    0x5 => ops::sub_vx_vy(self, x, y),
                    0x6 => ops::set_vx_to_vx_shr_1(self, x),
                    0xE => ops::set_vx_to_vx_shl_1(self, x),
                    _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
                }
            },
            0x9 => match n {
                0x0 => ops::skip_next_vx_ne_vy(self, x as uint, y as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA => ops::set_i(self, nnn),
            0xC => ops::set_vx_rand_and(self, x as uint, kk),
            0xD => ops::display_sprite(self, x as uint, y as uint, n as uint),
            0xE => match kk {
                0xA1 => ops::skip_next_key_vx_not_pressed(self, x as uint),
                0x9E => ops::skip_next_key_vx_pressed(self, x as uint),
                _ => fail!("Unknown 0xEXXX instruction: {:x}", ins)
            },
            0xF => {
                match kk {
                    0x07 => ops::set_vx_to_delay_timer(self, x as uint),
                    0x0A => ops::wait_for_keypress_store_in_vx(self, x as uint),
                    0x15 => ops::set_delay_timer(self, x as uint),
                    0x18 => ops::set_sound_timer(self, x as uint),
                    0x1E => ops::add_vx_to_i(self, x as uint),
                    0x29 => ops::set_i_to_loc_of_digit_vx(self, x as uint),
                    0x33 => ops::store_bcd_of_vx_to_i(self, x as uint),
                    0x55 => ops::copy_v0_through_vx_to_mem(self, x as uint),
                    0x65 => ops::read_v0_through_vx_from_mem(self, x as uint),
                    _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
                }
            },
            _ => fail!("Unknown instruction: {:04x}", ins)
        }
    }

    fn fetch_ins(&mut self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        self.pc += 2;
        b1 as u16 << 8 | b2 as u16
    }

    /// Press a key on the hexadecimal keypad
    ///
    /// `key` should be in the range `0..15`
    pub fn press_key(&mut self, key: uint) {
        assert!(key <= 15);
        self.keys[key] = true;
        if self.keypress_wait.wait {
            self.v[self.keypress_wait.vx] = key as u8;
            self.keypress_wait.wait = false;
        }
    }

    /// Release a key on the hexadecimal keypad
    ///
    /// `key` should be in the range `0..15`
    pub fn release_key(&mut self, key: uint) {
        assert!(key <= 15);
        self.keys[key] = false;
    }

    /// Decrement the sound and delay timers
    ///
    /// They should be decremented at a rate of 60 Hz
    pub fn decrement_timers(&mut self) {
        if self.delay_timer > 0 {
            self.delay_timer -= 1;
        }
        if self.sound_timer > 0 {
            self.sound_timer -= 1;
        }
    }
}
<|endoftext|>


<commit_before>//! Pure Rust implementation of Ryū, an algorithm to quickly convert floating
//! point numbers to decimal strings.
//!
//! The PLDI'18 paper [*Ryū: fast float-to-string conversion*][paper] by Ulf
//! Adams includes a complete correctness proof of the algorithm. The paper is
//! available under the creative commons CC-BY-SA license.
//!
//! This Rust implementation is a line-by-line port of Ulf Adams' implementation
//! in C, [https://github.com/ulfjack/ryu][upstream]. The [`ryu::raw`][raw]
//! module exposes exactly the API and formatting of the C implementation as
//! unsafe pure Rust functions. There is additionally a safe API as demonstrated
//! in the example code below. The safe API uses the same underlying Ryū
//! algorithm but diverges from the formatting of the C implementation to
//! produce more human-readable output, for example `0.3` rather than `3E-1`.
//!
//! [paper]: https://dl.acm.org/citation.cfm?id=3192369
//! [upstream]: https://github.com/ulfjack/ryu
//! [raw]: raw/index.html
//!
//! # Examples
//!
//! ```rust
//! extern crate ryu;
//!
//! fn main() {
//!     let mut buffer = ryu::Buffer::new();
//!     let printed = buffer.format(1.234);
//!     assert_eq!(printed, "1.234");
//! }
//! ```

#![no_std]
#![cfg_attr(feature = "no-panic", feature(use_extern_macros))]
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cast_lossless,
        cyclomatic_complexity,
        many_single_char_names,
        needless_pass_by_value,
        unreadable_literal,
    )
)]

#[cfg(feature = "no-panic")]
extern crate no_panic;

mod buffer;
mod common;
mod d2s;
#[cfg(not(feature = "small"))]
mod d2s_full_table;
#[cfg(feature = "small")]
mod d2s_small_table;
mod digit_table;
mod f2s;
#[cfg(not(integer128))]
mod mulshift128;
mod pretty;

pub use buffer::{Buffer, Float};

/// Unsafe functions that exactly mirror the API of the C implementation of Ryū.
pub mod raw {
    pub use d2s::d2s_buffered_n;
    pub use f2s::f2s_buffered_n;
}
<commit_msg>Point html docs to docs.rs<commit_after>//! Pure Rust implementation of Ryū, an algorithm to quickly convert floating
//! point numbers to decimal strings.
//!
//! The PLDI'18 paper [*Ryū: fast float-to-string conversion*][paper] by Ulf
//! Adams includes a complete correctness proof of the algorithm. The paper is
//! available under the creative commons CC-BY-SA license.
//!
//! This Rust implementation is a line-by-line port of Ulf Adams' implementation
//! in C, [https://github.com/ulfjack/ryu][upstream]. The [`ryu::raw`][raw]
//! module exposes exactly the API and formatting of the C implementation as
//! unsafe pure Rust functions. There is additionally a safe API as demonstrated
//! in the example code below. The safe API uses the same underlying Ryū
//! algorithm but diverges from the formatting of the C implementation to
//! produce more human-readable output, for example `0.3` rather than `3E-1`.
//!
//! [paper]: https://dl.acm.org/citation.cfm?id=3192369
//! [upstream]: https://github.com/ulfjack/ryu
//! [raw]: raw/index.html
//!
//! # Examples
//!
//! ```rust
//! extern crate ryu;
//!
//! fn main() {
//!     let mut buffer = ryu::Buffer::new();
//!     let printed = buffer.format(1.234);
//!     assert_eq!(printed, "1.234");
//! }
//! ```

#![no_std]
#![doc(html_root_url = "https://docs.rs/ryu/0.0.0")]
#![cfg_attr(feature = "no-panic", feature(use_extern_macros))]
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cast_lossless,
        cyclomatic_complexity,
        many_single_char_names,
        needless_pass_by_value,
        unreadable_literal,
    )
)]

#[cfg(feature = "no-panic")]
extern crate no_panic;

mod buffer;
mod common;
mod d2s;
#[cfg(not(feature = "small"))]
mod d2s_full_table;
#[cfg(feature = "small")]
mod d2s_small_table;
mod digit_table;
mod f2s;
#[cfg(not(integer128))]
mod mulshift128;
mod pretty;

pub use buffer::{Buffer, Float};

/// Unsafe functions that exactly mirror the API of the C implementation of Ryū.
pub mod raw {
    pub use d2s::d2s_buffered_n;
    pub use f2s::f2s_buffered_n;
}
<|endoftext|>