<commit_before>use crate::applog;
use crate::error;
use crate::message::*;
use crate::structs::SocketConfig as Config;
use std::os::unix::net::UnixDatagram;
use std::time::Duration;

#[derive(Debug)]
pub struct Connection {
    pub conf: Config,
    conn: UnixDatagram,
}

impl Connection {
    pub fn new(logid: &str) -> Result<Self, error::Error> {
        applog::init(Some("sectora"));
        log::debug!("{}", logid);
        let conf = Config::new();
        let conn = Self::connect_daemon(&conf)?;
        Ok(Self { conf, conn })
    }

    fn socket_path(conf: &Config) -> String { format!("{}/{}", &conf.socket_dir, std::process::id()) }

    fn connect_daemon(conf: &Config) -> Result<UnixDatagram, error::Error> {
        let socket = UnixDatagram::bind(&Self::socket_path(conf))?;
        log::debug!("{:?}", socket);
        socket.set_read_timeout(Some(Duration::from_secs(5)))?;
        socket.connect(&conf.socket_path)?;
        Ok(socket)
    }

    pub fn communicate(&self, msg: ClientMessage) -> Result<DaemonMessage, error::Error> {
        self.conn.send(msg.to_string().as_bytes())?;
        let mut buf = [0u8; 4096];
        let recv_cnt = match self.conn.recv(&mut buf) {
            Ok(cnt) => cnt,
            Err(e) => {
                log::debug!("ERROR: failed to recv msg, {}", e);
                return Err(error::Error::from(e));
            }
        };
        let s = String::from_utf8(buf[..recv_cnt].to_vec()).unwrap();
        log::debug!("recieved: {}", s);
        Ok(s.parse::<DaemonMessage>()?)
    }
}

impl Drop for Connection {
    fn drop(&mut self) { std::fs::remove_file(&Self::socket_path(&self.conf)).unwrap_or_default(); }
}
<commit_msg>Unpublish the field<commit_after>use crate::applog;
use crate::error;
use crate::message::*;
use crate::structs::SocketConfig as Config;
use std::os::unix::net::UnixDatagram;
use std::time::Duration;

#[derive(Debug)]
pub struct Connection {
    conf: Config,
    conn: UnixDatagram,
}

impl Connection {
    pub fn new(logid: &str) -> Result<Self, error::Error> {
        applog::init(Some("sectora"));
        log::debug!("{}", logid);
        let conf = Config::new();
        let conn = Self::connect_daemon(&conf)?;
        Ok(Self { conf, conn })
    }

    fn socket_path(conf: &Config) -> String { format!("{}/{}", &conf.socket_dir, std::process::id()) }

    fn connect_daemon(conf: &Config) -> Result<UnixDatagram, error::Error> {
        let socket = UnixDatagram::bind(&Self::socket_path(conf))?;
        log::debug!("{:?}", socket);
        socket.set_read_timeout(Some(Duration::from_secs(5)))?;
        socket.connect(&conf.socket_path)?;
        Ok(socket)
    }

    pub fn communicate(&self, msg: ClientMessage) -> Result<DaemonMessage, error::Error> {
        self.conn.send(msg.to_string().as_bytes())?;
        let mut buf = [0u8; 4096];
        let recv_cnt = match self.conn.recv(&mut buf) {
            Ok(cnt) => cnt,
            Err(e) => {
                log::debug!("ERROR: failed to recv msg, {}", e);
                return Err(error::Error::from(e));
            }
        };
        let s = String::from_utf8(buf[..recv_cnt].to_vec()).unwrap();
        log::debug!("recieved: {}", s);
        Ok(s.parse::<DaemonMessage>()?)
    }
}

impl Drop for Connection {
    fn drop(&mut self) { std::fs::remove_file(&Self::socket_path(&self.conf)).unwrap_or_default(); }
}
<|endoftext|>


<commit_before>use std::io::{Open, Read, Truncate, ReadWrite, SeekEnd, SeekSet};
use std::io::fs::File;
use std::mem::size_of;
use std::io::Decorator;
use std::vec;
use std::io::mem::{BufReader, MemWriter};
use std::io::io_error;

static VERSION: u8 = 1;

// some definitions from kissdb.h

struct Kissdb {
    hash_table_size : u64,
    key_size : u64,
    value_size : u64,
    num_hash_tables : u64,
    hash_tables : ~[u64],
    f : File
}

enum OpenMode {
    ReadOnly,
    RW,
    RWCreate,
    RWReplace
}

fn kissdb_hash(b : &[u8]) -> u64 {
    let mut hash = 5381;
    for &bx in b.iter() {
        hash = ((hash << 5) + hash) + (bx as u64);
    }
    hash
}

fn kissdb_open(
    path : &Path,
    orig_mode : OpenMode,
    mut hash_table_size : u64,
    mut key_size : u64,
    mut value_size : u64
        ) -> Option<~Kissdb>
{
    let (mode, access) = match orig_mode {
        ReadOnly =>  (Open, Read), // rb
        RW => (Open, ReadWrite), // r+b
        RWCreate =>  (Open, ReadWrite), // r+b
        RWReplace => (Truncate, ReadWrite) // w+b
    };
    let f_ = File::open_mode(path, mode, access).or_else(|| {
            match orig_mode {
                RWCreate => File::open_mode(path, Truncate, ReadWrite),
                _ => None
            }
        });

    let mut f = match f_ {
        Some(f) => f,
        None => return None
    };
    let kissdb_header_size = ((size_of::<u64>() * 3) + 4) as u64;

    f.seek(0, SeekEnd);
    if (f.tell() < kissdb_header_size) {
        if hash_table_size > 0 && key_size > 0
            && value_size >0
            {
            f.seek(0, SeekSet);
            let tmp2 = ['K' as u8, 'd' as u8, 'R' as u8, VERSION];
            f.write(tmp2);
            f.write_le_u64(hash_table_size);
            f.write_le_u64(key_size);
            f.write_le_u64(value_size);
            f.flush();
        }
        else {
            fail!()
        }
    }
    else {
        f.seek(0, SeekSet);
        let mut tmp2 = [0, ..4];
        if f.read(tmp2) != Some(4) { fail!() }
        if tmp2 != ['K' as u8, 'd' as u8, 'R' as u8, VERSION] { fail!() }
        hash_table_size = f.read_le_u64();
        key_size = f.read_le_u64();
        value_size = f.read_le_u64();
    }

    let mut num_hash_tables = 0;

    let mut hash_tables = ~[];
    let mut h;

    while ({h = f.read_one_hash_table(hash_table_size); h.is_some()})
    {
        let httmp = h.unwrap();
        hash_tables.push_all(httmp);
        num_hash_tables = num_hash_tables + 1;
        if (httmp[hash_table_size] > 0) {
            f.seek(httmp[hash_table_size] as i64, SeekSet)
        }
        else {
            break;
        }
    }

    debug!("key_size = {:s}", key_size.to_str());

    Some(~Kissdb {hash_table_size : hash_table_size,
                  key_size : key_size,
                  value_size : value_size,
                  num_hash_tables : num_hash_tables,
                  hash_tables : hash_tables,
                  f : f})
}

trait File2 {
    fn read_one_hash_table(&mut self, u64) -> Option<~[u64]>;
    fn write_one_hash_table(&mut self, &[u64]);
}

impl File2 for File {
    fn read_one_hash_table(&mut self,
                           hash_table_size : u64) -> Option<~[u64]> {
        let bs = (hash_table_size + 1) as uint * size_of::<u64>();
        let mut err = false;

        io_error::cond.trap(|_| err = true ).inside(|| {
            let buf = self.read_bytes(bs);
            let mut br = BufReader::new(buf);
            if err { None }
            else {
                Some(vec::from_fn((hash_table_size + 1) as uint,
                                  |_| br.read_le_u64()))
            }
        })
    }
    fn write_one_hash_table(&mut self, ht : &[u64]) {
        for &x in ht.iter() {
            self.write_le_u64(x);
        }
    }
}

trait Kdb {
    fn kissdb_get(&mut self, key : &[u8]) -> Option<~[u8]>;
    fn kissdb_put(&mut self, key : &[u8], value : &[u8]) -> bool;
}

impl Kdb for Kissdb {
    fn kissdb_get(&mut self, key : &[u8]) -> Option<~[u8]> {
        if key.len() != self.key_size as uint { return None }
        let hash = kissdb_hash(key) % self.hash_table_size;
        let hash_tables = &self.hash_tables;
        for i in range(0, self.num_hash_tables) {
            let offset = hash_tables[i * (self.hash_table_size + 1) + hash] as i64;
            if (offset == 0) { return None }; // yeah, 0 is being special. never used normally.
            self.f.seek(offset as i64, SeekSet);

            let klen = self.key_size as uint;
            let tmp = self.f.read_bytes(klen);

            if key == tmp {
                return Some(self.f.read_bytes(self.value_size as uint))
            }
        }
        None
    }

    fn kissdb_put(&mut self, key : &[u8], value : &[u8]) -> bool {
        if key.len() != self.key_size as uint { fail!() }
        if value.len() != self.value_size as uint { fail!() }
        let hash = kissdb_hash(key) % self.hash_table_size;
        let mut last_ht_offset = ((size_of::<u64>() * 3) + 4) as u64;
        let mut ht_offset = last_ht_offset;

        for i in range(0, self.num_hash_tables) {
            let offset = self.hash_tables[(self.hash_table_size + 1) * i + hash] as i64;
            if (offset != 0) { // yes, 0 is treated special. 0 will never used normally
                /* rewrite if already exists */
                self.f.seek(offset, SeekSet);

                let klen = self.key_size as uint;
                let tmp = self.f.read_bytes(klen);
                if key == tmp {
                    self.f.write(value);
                    self.f.flush();
                    return true; /* success */
                }
                // put_no_match_next_hash_table:
                last_ht_offset = ht_offset;
                ht_offset = self.hash_tables[(self.hash_table_size + 1) * i + self.hash_table_size];
                //cmp != , should be put_no_match_next_hash_table
            } else {
                /* add if an empty hash table slot is discovered */
                self.f.seek(0, SeekEnd);
                let end_offset = self.f.tell();

                // debug!("endoffset = {:s}",endoffset.to_str());
                self.f.write(key);
                self.f.write(value);

                self.f.seek(ht_offset as i64 + (size_of::<u64>() as i64 * hash as i64), SeekSet);
                self.f.write_le_u64(end_offset);
                self.hash_tables[(self.hash_table_size + 1) * i + hash] = end_offset;

                self.f.flush();

                return true; /* success */
            }
        }

        /* if no existing slots, add a new page of hash table entries */
        debug!("add new page");
        self.f.seek(0, SeekEnd);
        let endoffset = self.f.tell();

        let mut new_table = vec::from_elem(self.hash_table_size as uint + 1, 0 as u64);
        let hash_table_size_bytes = size_of::<u64>() as u64 * (self.hash_table_size + 1);
        new_table[hash] = endoffset + hash_table_size_bytes; // where key, value are to be written
        self.f.write_one_hash_table(new_table);
        self.hash_tables.push_all_move(new_table);

        debug!("writing at {:s}", self.f.tell().to_str());
        self.f.write(key);
        self.f.write(value);

        if self.num_hash_tables > 0 {
            self.f.seek(last_ht_offset as i64 + (size_of::<u64>() as i64 * self.hash_table_size as i64), SeekSet);
            self.f.write_le_u64(endoffset);
            // mimick rereading
            self.hash_tables[((self.hash_table_size + 1) * (self.num_hash_tables - 1)) + self.hash_table_size] = endoffset;
        }

        self.num_hash_tables = self.num_hash_tables + 1;
        self.f.flush();
        true /* success */
    }

}

fn main()
{
    println("Opening new empty database test.db...");
    {
        let mut db = kissdb_open(&Path::new("test.db"), RWReplace, 1024, 8, size_of::<u64>() as u64).unwrap();

        println("Adding and then re-getting 10000 64-byte values...");

        let mut v = [0,0,0,0,0,0,0,0];

        for i in range(0, 10000) {
            for j in range(0, 8) {
                v[j] = i as u8;
            }
            let mut key = MemWriter::new();
            key.write_le_u64(i as u64);
            let key_buf = key.inner();
            db.kissdb_put(key_buf, v);
            let gotten = db.kissdb_get(key_buf).unwrap();
            for j in range(0, 8) {
                if gotten[j] != i as u8 { fail!() }
            }
        }
        println("Getting 10000 64-byte values...");

        for i in range(0, 10000) {
            let mut key = MemWriter::new();
            key.write_le_u64(i as u64);
            let gotten = db.kissdb_get(key.inner()).unwrap();
            for j in range(0,8) {
                if gotten[j] != i as u8 { fail!() }
            }
        }

        println("closing and re-opening database in read-only mode...");
    }

    {
        let mut db = kissdb_open(&Path::new("test.db"), ReadOnly, 1024, 8, size_of::<u64>() as u64).unwrap();

        println("Getting 10000 64-byte values...");

        for i in range(0, 10000) {
            let mut key = MemWriter::new();
            key.write_le_u64(i as u64);
            let gotten = db.kissdb_get(key.inner()).unwrap();
            for j in range(0,8) {
                if gotten[j] != i as u8 { fail!() }
            }
        }
    }

    println("Iterator not implemented ...");

    println("All tests OK!");
}
<commit_msg>change main to test<commit_after>use std::io::{Open, Read, Truncate, ReadWrite, SeekEnd, SeekSet};
use std::io::fs::File;
use std::mem::size_of;
use std::io::Decorator;
use std::vec;
use std::io::mem::{BufReader, MemWriter};
use std::io::io_error;

static VERSION: u8 = 1;

// some definitions from kissdb.h

struct Kissdb {
    hash_table_size : u64,
    key_size : u64,
    value_size : u64,
    num_hash_tables : u64,
    hash_tables : ~[u64],
    f : File
}

enum OpenMode {
    ReadOnly,
    RW,
    RWCreate,
    RWReplace
}

fn kissdb_hash(b : &[u8]) -> u64 {
    let mut hash = 5381;
    for &bx in b.iter() {
        hash = ((hash << 5) + hash) + (bx as u64);
    }
    hash
}

fn kissdb_open(
    path : &Path,
    orig_mode : OpenMode,
    mut hash_table_size : u64,
    mut key_size : u64,
    mut value_size : u64
        ) -> Option<~Kissdb>
{
    let (mode, access) = match orig_mode {
        ReadOnly =>  (Open, Read), // rb
        RW => (Open, ReadWrite), // r+b
        RWCreate =>  (Open, ReadWrite), // r+b
        RWReplace => (Truncate, ReadWrite) // w+b
    };
    let f_ = File::open_mode(path, mode, access).or_else(|| {
            match orig_mode {
                RWCreate => File::open_mode(path, Truncate, ReadWrite),
                _ => None
            }
        });

    let mut f = match f_ {
        Some(f) => f,
        None => return None
    };
    let kissdb_header_size = ((size_of::<u64>() * 3) + 4) as u64;

    f.seek(0, SeekEnd);
    if (f.tell() < kissdb_header_size) {
        if hash_table_size > 0 && key_size > 0
            && value_size >0
            {
            f.seek(0, SeekSet);
            let tmp2 = ['K' as u8, 'd' as u8, 'R' as u8, VERSION];
            f.write(tmp2);
            f.write_le_u64(hash_table_size);
            f.write_le_u64(key_size);
            f.write_le_u64(value_size);
            f.flush();
        }
        else {
            fail!()
        }
    }
    else {
        f.seek(0, SeekSet);
        let mut tmp2 = [0, ..4];
        if f.read(tmp2) != Some(4) { fail!() }
        if tmp2 != ['K' as u8, 'd' as u8, 'R' as u8, VERSION] { fail!() }
        hash_table_size = f.read_le_u64();
        key_size = f.read_le_u64();
        value_size = f.read_le_u64();
    }

    let mut num_hash_tables = 0;

    let mut hash_tables = ~[];
    let mut h;

    while ({h = f.read_one_hash_table(hash_table_size); h.is_some()})
    {
        let httmp = h.unwrap();
        hash_tables.push_all(httmp);
        num_hash_tables = num_hash_tables + 1;
        if (httmp[hash_table_size] > 0) {
            f.seek(httmp[hash_table_size] as i64, SeekSet)
        }
        else {
            break;
        }
    }

    debug!("key_size = {:s}", key_size.to_str());

    Some(~Kissdb {hash_table_size : hash_table_size,
                  key_size : key_size,
                  value_size : value_size,
                  num_hash_tables : num_hash_tables,
                  hash_tables : hash_tables,
                  f : f})
}

trait File2 {
    fn read_one_hash_table(&mut self, u64) -> Option<~[u64]>;
    fn write_one_hash_table(&mut self, &[u64]);
}

impl File2 for File {
    fn read_one_hash_table(&mut self,
                           hash_table_size : u64) -> Option<~[u64]> {
        let bs = (hash_table_size + 1) as uint * size_of::<u64>();
        let mut err = false;

        io_error::cond.trap(|_| err = true ).inside(|| {
            let buf = self.read_bytes(bs);
            let mut br = BufReader::new(buf);
            if err { None }
            else {
                Some(vec::from_fn((hash_table_size + 1) as uint,
                                  |_| br.read_le_u64()))
            }
        })
    }
    fn write_one_hash_table(&mut self, ht : &[u64]) {
        for &x in ht.iter() {
            self.write_le_u64(x);
        }
    }
}

trait Kdb {
    fn kissdb_get(&mut self, key : &[u8]) -> Option<~[u8]>;
    fn kissdb_put(&mut self, key : &[u8], value : &[u8]) -> bool;
}

impl Kdb for Kissdb {
    fn kissdb_get(&mut self, key : &[u8]) -> Option<~[u8]> {
        if key.len() != self.key_size as uint { return None }
        let hash = kissdb_hash(key) % self.hash_table_size;
        let hash_tables = &self.hash_tables;
        for i in range(0, self.num_hash_tables) {
            let offset = hash_tables[i * (self.hash_table_size + 1) + hash] as i64;
            if (offset == 0) { return None }; // yeah, 0 is being special. never used normally.
            self.f.seek(offset as i64, SeekSet);

            let klen = self.key_size as uint;
            let tmp = self.f.read_bytes(klen);

            if key == tmp {
                return Some(self.f.read_bytes(self.value_size as uint))
            }
        }
        None
    }

    fn kissdb_put(&mut self, key : &[u8], value : &[u8]) -> bool {
        if key.len() != self.key_size as uint { fail!() }
        if value.len() != self.value_size as uint { fail!() }
        let hash = kissdb_hash(key) % self.hash_table_size;
        let mut last_ht_offset = ((size_of::<u64>() * 3) + 4) as u64;
        let mut ht_offset = last_ht_offset;

        for i in range(0, self.num_hash_tables) {
            let offset = self.hash_tables[(self.hash_table_size + 1) * i + hash] as i64;
            if (offset != 0) { // yes, 0 is treated special. 0 will never used normally
                /* rewrite if already exists */
                self.f.seek(offset, SeekSet);

                let klen = self.key_size as uint;
                let tmp = self.f.read_bytes(klen);
                if key == tmp {
                    self.f.write(value);
                    self.f.flush();
                    return true; /* success */
                }
                // put_no_match_next_hash_table:
                last_ht_offset = ht_offset;
                ht_offset = self.hash_tables[(self.hash_table_size + 1) * i + self.hash_table_size];
                //cmp != , should be put_no_match_next_hash_table
            } else {
                /* add if an empty hash table slot is discovered */
                self.f.seek(0, SeekEnd);
                let end_offset = self.f.tell();

                // debug!("endoffset = {:s}",endoffset.to_str());
                self.f.write(key);
                self.f.write(value);

                self.f.seek(ht_offset as i64 + (size_of::<u64>() as i64 * hash as i64), SeekSet);
                self.f.write_le_u64(end_offset);
                self.hash_tables[(self.hash_table_size + 1) * i + hash] = end_offset;

                self.f.flush();

                return true; /* success */
            }
        }

        /* if no existing slots, add a new page of hash table entries */
        debug!("add new page");
        self.f.seek(0, SeekEnd);
        let endoffset = self.f.tell();

        let mut new_table = vec::from_elem(self.hash_table_size as uint + 1, 0 as u64);
        let hash_table_size_bytes = size_of::<u64>() as u64 * (self.hash_table_size + 1);
        new_table[hash] = endoffset + hash_table_size_bytes; // where key, value are to be written
        self.f.write_one_hash_table(new_table);
        self.hash_tables.push_all_move(new_table);

        debug!("writing at {:s}", self.f.tell().to_str());
        self.f.write(key);
        self.f.write(value);

        if self.num_hash_tables > 0 {
            self.f.seek(last_ht_offset as i64 + (size_of::<u64>() as i64 * self.hash_table_size as i64), SeekSet);
            self.f.write_le_u64(endoffset);
            // mimick rereading
            self.hash_tables[((self.hash_table_size + 1) * (self.num_hash_tables - 1)) + self.hash_table_size] = endoffset;
        }

        self.num_hash_tables = self.num_hash_tables + 1;
        self.f.flush();
        true /* success */
    }

}

#[test]
fn original_test()
{
    println("Opening new empty database test.db...");
    {
        let mut db = kissdb_open(&Path::new("test.db"), RWReplace, 1024, 8, size_of::<u64>() as u64).unwrap();

        println("Adding and then re-getting 10000 64-byte values...");

        let mut v = [0,0,0,0,0,0,0,0];

        for i in range(0, 10000) {
            for j in range(0, 8) {
                v[j] = i as u8;
            }
            let mut key = MemWriter::new();
            key.write_le_u64(i as u64);
            let key_buf = key.inner();
            db.kissdb_put(key_buf, v);
            let gotten = db.kissdb_get(key_buf).unwrap();
            for j in range(0, 8) {
                if gotten[j] != i as u8 { fail!() }
            }
        }
        println("Getting 10000 64-byte values...");

        for i in range(0, 10000) {
            let mut key = MemWriter::new();
            key.write_le_u64(i as u64);
            let gotten = db.kissdb_get(key.inner()).unwrap();
            for j in range(0,8) {
                if gotten[j] != i as u8 { fail!() }
            }
        }

        println("closing and re-opening database in read-only mode...");
    }

    {
        let mut db = kissdb_open(&Path::new("test.db"), ReadOnly, 1024, 8, size_of::<u64>() as u64).unwrap();

        println("Getting 10000 64-byte values...");

        for i in range(0, 10000) {
            let mut key = MemWriter::new();
            key.write_le_u64(i as u64);
            let gotten = db.kissdb_get(key.inner()).unwrap();
            for j in range(0,8) {
                if gotten[j] != i as u8 { fail!() }
            }
        }
    }

    println("Iterator not implemented ...");

    println("All tests OK!");
}
<|endoftext|>


<commit_before>use std;
use std::fmt;

use docopt;

#[derive(Debug, Clone)]
pub struct Error {
    pub message: String,
    pub exit_code: i32,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.message.fmt(f)
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        &self.message
    }

    fn cause(&self) -> Option<&std::error::Error> {
        None
    }
}

impl From<docopt::Error> for Error {
    fn from(error: docopt::Error) -> Error {
        match error {
            docopt::Error::WithProgramUsage(err_box, message) => {
                let exit_code = match *err_box {
                    docopt::Error::Usage(..) => panic!("Invalid usage string!"),
                    docopt::Error::Argv(..) => 1,
                    docopt::Error::NoMatch => 1,
                    docopt::Error::Decode(..) => 1,
                    docopt::Error::WithProgramUsage(..) => unreachable!(),
                    docopt::Error::Help => 0,
                    docopt::Error::Version(..) => 0,
                };
                Error { message: message, exit_code: exit_code }
            },
            _ => {
                panic!("Unknown docopt error!");
            }
        }
    }
}

pub type Result<T> = std::result::Result<T, Error>;
<commit_msg>Improve error handling in args<commit_after>use std;
use std::fmt;
use std::rc::Rc;

use docopt;

#[derive(Debug, Clone)]
pub struct Error {
    pub message: String,
    pub exit_code: i32,
    pub cause: Rc<docopt::Error>,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.message.fmt(f)
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        &self.message
    }

    fn cause(&self) -> Option<&std::error::Error> {
        Some(&*self.cause)
    }
}

impl From<docopt::Error> for Error {
    fn from(error: docopt::Error) -> Error {
        let params = match error {
            docopt::Error::WithProgramUsage(ref err_box, ref message) => {
                let message = message.clone();
                let exit_code = match **err_box {
                    docopt::Error::Usage(..) => panic!("Invalid usage string!"),
                    docopt::Error::Argv(..) => 1,
                    docopt::Error::NoMatch => 1,
                    docopt::Error::Decode(..) => 1,
                    docopt::Error::WithProgramUsage(..) => unreachable!(),
                    docopt::Error::Help => 0,
                    docopt::Error::Version(..) => 0,
                };

                Some((message, exit_code))
            },
            _ => None,
        };

        match params {
            Some((message, exit_code)) => {
                Error {
                    message: message,
                    exit_code: exit_code,
                    cause: Rc::new(error),
                }
            },
            None => {
                panic!("Unknown docopt error!");
            }
        }
    }
}

pub type Result<T> = std::result::Result<T, Error>;
<|endoftext|>


<commit_before>//!

use super::list_of_types::{ListedTypeIndex, Sublist};
use std::ptr::{self, Shared};
use std::sync::atomic::AtomicUsize;
use std::any::Any;

struct ThinArcInnerAlways <List: Sublist, AlwaysData: Any> {
  index: ListedTypeIndex <List>,
  references: AtomicUsize,
  data: AlwaysData,
}
#[repr(C)]
struct ThinArcInner <List: Sublist, AlwaysData: Any, SpecificData: Any> {
  always: ThinArcInnerAlways <List, AlwaysData>,
  data: SpecificData,
}
pub struct ThinArc <List: Sublist, AlwaysData: Any> {
  pointer: Shared <ThinArcInnerAlways <List, AlwaysData>>,
}
<commit_msg>Implemented more of ThinArc<commit_after>//!

use super::list_of_types::{ListedTypeIndex, Sublist};
use std::ptr::{self, Shared};
use std::sync::atomic::AtomicUsize;
use std::any::Any;
use std::mem;

struct ThinArcInnerAlways <List: Sublist, AlwaysData: Any> {
  index: ListedTypeIndex <List>,
  references: AtomicUsize,
  data: AlwaysData,
}
#[repr(C)]
struct ThinArcInner <List: Sublist, AlwaysData: Any, SpecificData: Any> {
  always: ThinArcInnerAlways <List, AlwaysData>,
  data: SpecificData,
}
pub struct DynamicThinArc <List: Sublist, AlwaysData: Any> {
  pointer: Shared <ThinArcInnerAlways <List, AlwaysData>>,
}
pub struct ThinArc <List: Sublist, AlwaysData: Any, SpecificData: Any> {
  pointer: Shared <ThinArcInner <List, AlwaysData, SpecificData>>,
}

impl <List: Sublist, AlwaysData: Any, SpecificData: Any> ThinArc <List, AlwaysData, SpecificData> {
  fn erase_type (self)->DynamicThinArc <List, AlwaysData> {
    DynamicThinArc {pointer: unsafe {mem::transmute (self.pointer)}}
  }
}
impl <List: Sublist, AlwaysData: Any> DynamicThinArc <List, AlwaysData> {
  /// If it's not the correct type, return the original DynamicThinArc
  fn downcast <SpecificData: Any> (self)->Result <ThinArc <List, AlwaysData, SpecificData>, DynamicThinArc <List, AlwaysData>> {
    if unsafe {self.pointer.as_ref().index} == List::index:: <SpecificData> () {
      Ok (ThinArc {pointer: unsafe {mem::transmute (self.pointer)}})
    }
    else {
      Err (self)
    }
  }
}
<|endoftext|>


<commit_before>/// Filters operating on numbers
use std::collections::HashMap;

use serde_json::value::{Value, to_value};
use humansize::{FileSize, file_size_opts};

use errors::Result;

/// Returns a suffix if the value is greater or equal than 2. Suffix defaults to `s`
pub fn pluralize(value: Value, args: HashMap<String, Value>) -> Result<Value> {
    let num = try_get_value!("pluralize", "value", f64, value);
    let suffix = match args.get("suffix") {
        Some(val) => try_get_value!("pluralize", "suffix", String, val),
        None => "s".to_string(),
    };

    if num >= 2.0 {
        Ok(to_value(&suffix).unwrap())
    } else {
        Ok(to_value(&"").unwrap())
    }
}

/// Returns a rounded number using the `method` arg and `precision` given.
/// `method` defaults to `common` which will round to the nearest number.
/// `ceil` and `floor` are also available as method.
/// `precision` defaults to `0`, meaning it will round to an integer
pub fn round(value: Value, args: HashMap<String, Value>) -> Result<Value> {
    let num = try_get_value!("round", "value", f64, value);
    let method = match args.get("method") {
        Some(val) => try_get_value!("round", "method", String, val),
        None => "common".to_string(),
    };
    let precision = match args.get("precision") {
        Some(val) => try_get_value!("round", "precision", i32, val),
        None => 0,
    };
    let multiplier = if precision == 0 { 1.0 } else { 10.0_f64.powi(precision) } ;

    match method.as_ref() {
        "common" => Ok(to_value((multiplier * num).round() / multiplier).unwrap()),
        "ceil" => Ok(to_value((multiplier * num).ceil() / multiplier).unwrap()),
        "floor" => Ok(to_value((multiplier * num).floor() / multiplier).unwrap()),
        _ => bail!(
                "Filter `round` received an incorrect value for arg `method`: got `{:?}`, \
                only common, ceil and floor are allowed",
                method
            )
    }
}

/// Returns a human-readable file size (i.e. '110 MB') from an integer
pub fn filesizeformat(value: Value, _: HashMap<String, Value>) -> Result<Value> {
    let num = try_get_value!("filesizeformat", "value", i64, value);
    match num
        .file_size(file_size_opts::CONVENTIONAL)
        .or_else(|_| Err(format!("Filter `filesizeformat` was called on a negative number: {}", num).into())) {
        Ok(r) => Ok(to_value(r).unwrap()),
        Err(e) => Err(e)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use serde_json::value::to_value;
    use super::*;

    #[test]
    fn test_pluralize_single() {
        let result = pluralize(to_value(1).unwrap(), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("").unwrap());
    }

    #[test]
    fn test_pluralize_multiple() {
        let result = pluralize(to_value(2).unwrap(), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("s").unwrap());
    }

    #[test]
    fn test_pluralize_multiple_custom_suffix() {
        let mut args = HashMap::new();
        args.insert("suffix".to_string(), to_value("es").unwrap());
        let result = pluralize(to_value(2).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("es").unwrap());
    }

    #[test]
    fn test_round_default() {
        let result = round(to_value(2.1).unwrap(), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(2.0).unwrap());
    }

    #[test]
    fn test_round_default_precision() {
        let mut args = HashMap::new();
        args.insert("precision".to_string(), to_value(2).unwrap());
        let result = round(to_value(3.15159265359).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(3.15).unwrap());
    }

    #[test]
    fn test_round_ceil() {
        let mut args = HashMap::new();
        args.insert("method".to_string(), to_value("ceil").unwrap());
        let result = round(to_value(2.1).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(3.0).unwrap());
    }

    #[test]
    fn test_round_ceil_precision() {
        let mut args = HashMap::new();
        args.insert("method".to_string(), to_value("ceil").unwrap());
        args.insert("precision".to_string(), to_value(1).unwrap());
        let result = round(to_value(2.11).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(2.2).unwrap());
    }

    #[test]
    fn test_round_floor() {
        let mut args = HashMap::new();
        args.insert("method".to_string(), to_value("floor").unwrap());
        let result = round(to_value(2.1).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(2.0).unwrap());
    }

    #[test]
    fn test_round_floor_precision() {
        let mut args = HashMap::new();
        args.insert("method".to_string(), to_value("floor").unwrap());
        args.insert("precision".to_string(), to_value(1).unwrap());
        let result = round(to_value(2.91).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(2.9).unwrap());
    }

    #[test]
    fn test_filesizeformat() {
        let args = HashMap::new();
        let result = filesizeformat(to_value(123456789).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("117.74 MB").unwrap());
    }
}
<commit_msg>Make pluralize return plural for 0 thing<commit_after>/// Filters operating on numbers
use std::collections::HashMap;

use serde_json::value::{Value, to_value};
use humansize::{FileSize, file_size_opts};

use errors::Result;

/// Returns a suffix if the value is greater or equal than 2. Suffix defaults to `s`
pub fn pluralize(value: Value, args: HashMap<String, Value>) -> Result<Value> {
    let num = try_get_value!("pluralize", "value", f64, value);
    let suffix = match args.get("suffix") {
        Some(val) => try_get_value!("pluralize", "suffix", String, val),
        None => "s".to_string(),
    };

    // English uses plural with zero or more than one thing
    if num == 0.0 || num >= 2.0 {
        Ok(to_value(&suffix).unwrap())
    } else {
        Ok(to_value(&"").unwrap())
    }
}

/// Returns a rounded number using the `method` arg and `precision` given.
/// `method` defaults to `common` which will round to the nearest number.
/// `ceil` and `floor` are also available as method.
/// `precision` defaults to `0`, meaning it will round to an integer
pub fn round(value: Value, args: HashMap<String, Value>) -> Result<Value> {
    let num = try_get_value!("round", "value", f64, value);
    let method = match args.get("method") {
        Some(val) => try_get_value!("round", "method", String, val),
        None => "common".to_string(),
    };
    let precision = match args.get("precision") {
        Some(val) => try_get_value!("round", "precision", i32, val),
        None => 0,
    };
    let multiplier = if precision == 0 { 1.0 } else { 10.0_f64.powi(precision) } ;

    match method.as_ref() {
        "common" => Ok(to_value((multiplier * num).round() / multiplier).unwrap()),
        "ceil" => Ok(to_value((multiplier * num).ceil() / multiplier).unwrap()),
        "floor" => Ok(to_value((multiplier * num).floor() / multiplier).unwrap()),
        _ => bail!(
                "Filter `round` received an incorrect value for arg `method`: got `{:?}`, \
                only common, ceil and floor are allowed",
                method
            )
    }
}

/// Returns a human-readable file size (i.e. '110 MB') from an integer
pub fn filesizeformat(value: Value, _: HashMap<String, Value>) -> Result<Value> {
    let num = try_get_value!("filesizeformat", "value", i64, value);
    match num
        .file_size(file_size_opts::CONVENTIONAL)
        .or_else(|_| Err(format!("Filter `filesizeformat` was called on a negative number: {}", num).into())) {
        Ok(r) => Ok(to_value(r).unwrap()),
        Err(e) => Err(e)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use serde_json::value::to_value;
    use super::*;

    #[test]
    fn test_pluralize_single() {
        let result = pluralize(to_value(1).unwrap(), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("").unwrap());
    }

    #[test]
    fn test_pluralize_multiple() {
        let result = pluralize(to_value(2).unwrap(), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("s").unwrap());
    }

    #[test]
    fn test_pluralize_zero() {
        let result = pluralize(to_value(0).unwrap(), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("s").unwrap());
    }

    #[test]
    fn test_pluralize_multiple_custom_suffix() {
        let mut args = HashMap::new();
        args.insert("suffix".to_string(), to_value("es").unwrap());
        let result = pluralize(to_value(2).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("es").unwrap());
    }

    #[test]
    fn test_round_default() {
        let result = round(to_value(2.1).unwrap(), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(2.0).unwrap());
    }

    #[test]
    fn test_round_default_precision() {
        let mut args = HashMap::new();
        args.insert("precision".to_string(), to_value(2).unwrap());
        let result = round(to_value(3.15159265359).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(3.15).unwrap());
    }

    #[test]
    fn test_round_ceil() {
        let mut args = HashMap::new();
        args.insert("method".to_string(), to_value("ceil").unwrap());
        let result = round(to_value(2.1).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(3.0).unwrap());
    }

    #[test]
    fn test_round_ceil_precision() {
        let mut args = HashMap::new();
        args.insert("method".to_string(), to_value("ceil").unwrap());
        args.insert("precision".to_string(), to_value(1).unwrap());
        let result = round(to_value(2.11).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(2.2).unwrap());
    }

    #[test]
    fn test_round_floor() {
        let mut args = HashMap::new();
        args.insert("method".to_string(), to_value("floor").unwrap());
        let result = round(to_value(2.1).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(2.0).unwrap());
    }

    #[test]
    fn test_round_floor_precision() {
        let mut args = HashMap::new();
        args.insert("method".to_string(), to_value("floor").unwrap());
        args.insert("precision".to_string(), to_value(1).unwrap());
        let result = round(to_value(2.91).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(2.9).unwrap());
    }

    #[test]
    fn test_filesizeformat() {
        let args = HashMap::new();
        let result = filesizeformat(to_value(123456789).unwrap(), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("117.74 MB").unwrap());
    }
}
<|endoftext|>


<commit_before>use git2;
use serde_json;

use std::str::FromStr;
use git2::{ Time };
use super::{ Oid };

#[derive(Deserialize)]
pub struct Review {
  timestamp: Option<String>,
  #[serde(rename="reviewRef")]
  review_ref: Option<String>,
  #[serde(rename="targetRef")]
  target_ref: Option<String>,
  requester: Option<String>,
  reviewers: Option<Vec<String>>,
  description: Option<String>,
  #[serde(rename="baseCommit")]
  base_commit: Option<String>,
}

pub struct Reviews<'r> {
  notes: git2::Notes<'r>,
}

impl Review {
  pub fn from_str(s: &str) -> Result<Review, serde_json::error::Error> {
    serde_json::de::from_str(s)
  }

  pub fn timestamp(&self) -> Option<Time> {
    self.timestamp.as_ref().and_then(|timestamp| FromStr::from_str(timestamp).ok().map(|time| Time::new(time, 0)))
  }

  pub fn review_ref(&self) -> Option<&String> {
    self.review_ref.as_ref()
  }

  pub fn target_ref(&self) -> Option<&String> {
    self.target_ref.as_ref()
  }

  pub fn requester(&self) -> Option<&String> {
    self.requester.as_ref()
  }

  pub fn reviewers(&self) -> Option<&Vec<String>> {
    self.reviewers.as_ref()
  }

  pub fn description(&self) -> Option<&String> {
    self.description.as_ref()
  }

  pub fn base_commit(&self) -> Option<&String> {
    self.base_commit.as_ref()
  }

}

impl<'r> Reviews<'r> {
  pub fn new(notes: git2::Notes<'r>) -> Reviews<'r> {
    Reviews {
      notes: notes,
    }
  }
}

impl<'r> Iterator for Reviews<'r> {
  type Item = Oid;

  fn next(&mut self) -> Option<Oid> {
    self.notes.next().map(|(_, id)| id)
  }

  fn size_hint(&self) -> (usize, Option<usize>) {
    self.notes.size_hint()
  }
}

<commit_msg>Take only the last object from a note<commit_after>use git2;
use serde_json;

use std::str::FromStr;
use git2::{ Time };
use super::{ Oid };

#[derive(Deserialize)]
pub struct Review {
  timestamp: Option<String>,
  #[serde(rename="reviewRef")]
  review_ref: Option<String>,
  #[serde(rename="targetRef")]
  target_ref: Option<String>,
  requester: Option<String>,
  reviewers: Option<Vec<String>>,
  description: Option<String>,
  #[serde(rename="baseCommit")]
  base_commit: Option<String>,
}

pub struct Reviews<'r> {
  notes: git2::Notes<'r>,
}

impl Review {
  pub fn from_str(s: &str) -> Result<Review, serde_json::error::Error> {
    serde_json::de::from_str(s.lines().rev().nth(0).unwrap())
  }

  pub fn timestamp(&self) -> Option<Time> {
    self.timestamp.as_ref().and_then(|timestamp| FromStr::from_str(timestamp).ok().map(|time| Time::new(time, 0)))
  }

  pub fn review_ref(&self) -> Option<&String> {
    self.review_ref.as_ref()
  }

  pub fn target_ref(&self) -> Option<&String> {
    self.target_ref.as_ref()
  }

  pub fn requester(&self) -> Option<&String> {
    self.requester.as_ref()
  }

  pub fn reviewers(&self) -> Option<&Vec<String>> {
    self.reviewers.as_ref()
  }

  pub fn description(&self) -> Option<&String> {
    self.description.as_ref()
  }

  pub fn base_commit(&self) -> Option<&String> {
    self.base_commit.as_ref()
  }

}

impl<'r> Reviews<'r> {
  pub fn new(notes: git2::Notes<'r>) -> Reviews<'r> {
    Reviews {
      notes: notes,
    }
  }
}

impl<'r> Iterator for Reviews<'r> {
  type Item = Oid;

  fn next(&mut self) -> Option<Oid> {
    self.notes.next().map(|(_, id)| id)
  }

  fn size_hint(&self) -> (usize, Option<usize>) {
    self.notes.size_hint()
  }
}

<|endoftext|>


<commit_before>use std::io::{Read, Take};
use std::mem;
use serde::de;
use serde::de::{DeserializeSeed, SeqAccess, Visitor};

use error::{Error, ResultE};
use super::iter_visitor::IterVisitor;
use super::osc_reader::OscReader;
use super::pkt_deserializer::PktDeserializer;
use super::prim_deserializer::PrimDeserializer;

/// Deserializes a single bundle, within a packet.
#[derive(Debug)]
pub struct BundleVisitor<'a, R: Read + 'a> {
    read: &'a mut Take<R>,
    state: State,
}

/// Which part of the bundle is being parsed
#[derive(Debug)]
enum State {
    /// Parsing the 64-bit OSC time tag
    TimeTag,
    /// Parsing the body of the bundle: OSC Bundle Elements
    Elements,
}

/// Struct to deserialize a single element from the OSC bundle
enum BundleField<'a, R: Read + 'a> {
    TimeTag((u32, u32)),
    Elements(&'a mut Take<R>),
}

/// Deserializes each item (message/bundle) within the bundle element sequence.
struct ElemAccessor<'a, R: Read + 'a> {
    read: &'a mut Take<R>,
}

impl<'a, R> BundleVisitor<'a, R>
    where R: Read + 'a
{
    pub fn new(read: &'a mut Take<R>) -> Self {
        Self {
            read: read,
            state: State::TimeTag,
        }
    }
}

impl<'de, 'a, R> SeqAccess<'de> for BundleVisitor<'a, R>
    where R: Read + 'a
{
    type Error = Error;
    fn next_element_seed<T>(&mut self, seed: T) -> ResultE<Option<T::Value>>
        where T: DeserializeSeed<'de>
    {
        if self.read.limit() == 0 {
            // end of bundle
            return Ok(None);
        }
        let elem = match mem::replace(&mut self.state, State::Elements) {
            State::TimeTag => BundleField::TimeTag(self.read.parse_timetag()?),
            State::Elements => BundleField::Elements(self.read),
            //State::Elements => BundleField::Packet(PktDeserializer::new(self.read)),
        };
        seed.deserialize(elem).map(Some)
    }
}

impl<'de, 'a, R> de::Deserializer<'de> for BundleField<'a, R>
    where R: Read + 'a
{
    type Error = Error;
    // deserializes a single item from the message, consuming self.
    fn deserialize_any<V>(self, visitor: V) -> ResultE<V::Value>
    where
        V: Visitor<'de>
    {
        match self {
            BundleField::TimeTag((sec, frac)) =>
                visitor.visit_seq(IterVisitor([sec, frac].into_iter().cloned()
                    .map(PrimDeserializer))),
            BundleField::Elements(mut read) =>
                visitor.visit_seq(ElemAccessor{ read }),
        }
    }

    // OSC messages are strongly typed, so we don't make use of any type hints.
    // More info: https://github.com/serde-rs/serde/blob/b7d6c5d9f7b3085a4d40a446eeb95976d2337e07/serde/src/macros.rs#L106
    forward_to_deserialize_any! {
        bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
        seq bytes byte_buf map unit_struct newtype_struct
        tuple_struct struct identifier tuple enum ignored_any
    }
}

impl<'de, 'a, R> SeqAccess<'de> for ElemAccessor<'a, R>
    where R: Read + 'a
{
    type Error = Error;
    fn next_element_seed<T>(&mut self, seed: T) -> ResultE<Option<T::Value>>
        where T: DeserializeSeed<'de>
    {
        // TODO: handle EOF by returning None
        seed.deserialize(&mut PktDeserializer::new(self.read)).map(Some)
    }
}
<commit_msg>Use `slice::iter` instead of `into_iter` to avoid future breakage<commit_after>use std::io::{Read, Take};
use std::mem;
use serde::de;
use serde::de::{DeserializeSeed, SeqAccess, Visitor};

use error::{Error, ResultE};
use super::iter_visitor::IterVisitor;
use super::osc_reader::OscReader;
use super::pkt_deserializer::PktDeserializer;
use super::prim_deserializer::PrimDeserializer;

/// Deserializes a single bundle, within a packet.
#[derive(Debug)]
pub struct BundleVisitor<'a, R: Read + 'a> {
    read: &'a mut Take<R>,
    state: State,
}

/// Which part of the bundle is being parsed
#[derive(Debug)]
enum State {
    /// Parsing the 64-bit OSC time tag
    TimeTag,
    /// Parsing the body of the bundle: OSC Bundle Elements
    Elements,
}

/// Struct to deserialize a single element from the OSC bundle
enum BundleField<'a, R: Read + 'a> {
    TimeTag((u32, u32)),
    Elements(&'a mut Take<R>),
}

/// Deserializes each item (message/bundle) within the bundle element sequence.
struct ElemAccessor<'a, R: Read + 'a> {
    read: &'a mut Take<R>,
}

impl<'a, R> BundleVisitor<'a, R>
    where R: Read + 'a
{
    pub fn new(read: &'a mut Take<R>) -> Self {
        Self {
            read: read,
            state: State::TimeTag,
        }
    }
}

impl<'de, 'a, R> SeqAccess<'de> for BundleVisitor<'a, R>
    where R: Read + 'a
{
    type Error = Error;
    fn next_element_seed<T>(&mut self, seed: T) -> ResultE<Option<T::Value>>
        where T: DeserializeSeed<'de>
    {
        if self.read.limit() == 0 {
            // end of bundle
            return Ok(None);
        }
        let elem = match mem::replace(&mut self.state, State::Elements) {
            State::TimeTag => BundleField::TimeTag(self.read.parse_timetag()?),
            State::Elements => BundleField::Elements(self.read),
            //State::Elements => BundleField::Packet(PktDeserializer::new(self.read)),
        };
        seed.deserialize(elem).map(Some)
    }
}

impl<'de, 'a, R> de::Deserializer<'de> for BundleField<'a, R>
    where R: Read + 'a
{
    type Error = Error;
    // deserializes a single item from the message, consuming self.
    fn deserialize_any<V>(self, visitor: V) -> ResultE<V::Value>
    where
        V: Visitor<'de>
    {
        match self {
            BundleField::TimeTag((sec, frac)) =>
                visitor.visit_seq(IterVisitor([sec, frac].iter().cloned()
                    .map(PrimDeserializer))),
            BundleField::Elements(mut read) =>
                visitor.visit_seq(ElemAccessor{ read }),
        }
    }

    // OSC messages are strongly typed, so we don't make use of any type hints.
    // More info: https://github.com/serde-rs/serde/blob/b7d6c5d9f7b3085a4d40a446eeb95976d2337e07/serde/src/macros.rs#L106
    forward_to_deserialize_any! {
        bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
        seq bytes byte_buf map unit_struct newtype_struct
        tuple_struct struct identifier tuple enum ignored_any
    }
}

impl<'de, 'a, R> SeqAccess<'de> for ElemAccessor<'a, R>
    where R: Read + 'a
{
    type Error = Error;
    fn next_element_seed<T>(&mut self, seed: T) -> ResultE<Option<T::Value>>
        where T: DeserializeSeed<'de>
    {
        // TODO: handle EOF by returning None
        seed.deserialize(&mut PktDeserializer::new(self.read)).map(Some)
    }
}
<|endoftext|>


<commit_before>//! `CountMinSketch` implementation.
use std::collections::hash_map::DefaultHasher;
use std::f64;
use std::fmt;
use std::hash::{BuildHasher, Hash};

use num_traits::{CheckedAdd, One, Unsigned, Zero};

use hash_utils::{HashIter, MyBuildHasherDefault};

/// Simple implementation of a
/// [Count-min sketch](https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch)
///
/// The type parameter `C` sets the type of the counter in the internal table and can be used to
/// reduce memory consumption when low counts are expected.
#[derive(Clone)]
pub struct CountMinSketch<C = usize, B = MyBuildHasherDefault<DefaultHasher>>
where
    C: CheckedAdd + Clone + One + Ord + Unsigned + Zero,
    B: BuildHasher + Clone + Eq,
{
    table: Vec<C>,
    w: usize,
    d: usize,
    buildhasher: B,
}

impl<C> CountMinSketch<C>
where
    C: CheckedAdd + Clone + One + Ord + Unsigned + Zero,
{
    /// Create new CountMinSketch based on table size.
    ///
    /// - `w` sets the number of columns
    /// - `d` sets the number of rows
    pub fn with_params(w: usize, d: usize) -> CountMinSketch<C> {
        let bh = MyBuildHasherDefault::<DefaultHasher>::default();
        Self::with_params_and_hasher(w, d, bh)
    }

    /// Create new CountMinSketch with the following properties for a point query:
    ///
    /// - `a` is the real count of observed objects
    /// - `a'` is the guessed count of observed objects
    /// - `N` is the total count in the internal table
    /// - `a <= a'` always holds
    /// - `a' <= a + epsilon * N` holds with `p > 1 - delta`
    ///
    /// The following conditions must hold:
    ///
    /// - `epsilon > 0`
    /// - `delta > 0` and `delta < 1`
    ///
    /// Panics when the input conditions do not hold.
    pub fn with_point_query_properties(epsilon: f64, delta: f64) -> CountMinSketch<C> {
        let bh = MyBuildHasherDefault::<DefaultHasher>::default();
        Self::with_point_query_properties_and_hasher(epsilon, delta, bh)
    }
}

impl<C, B> CountMinSketch<C, B>
where
    C: CheckedAdd + Clone + One + Ord + Unsigned + Zero,
    B: BuildHasher + Clone + Eq,
{
    /// Same as `with_params` but with a specific `BuildHasher`.
    pub fn with_params_and_hasher(w: usize, d: usize, buildhasher: B) -> CountMinSketch<C, B> {
        let table = vec![C::zero(); w.checked_mul(d).unwrap()];
        CountMinSketch {
            table: table,
            w: w,
            d: d,
            buildhasher: buildhasher,
        }
    }

    /// Same as `with_params_and_hasher` but with a specific `BuildHasher`.
    pub fn with_point_query_properties_and_hasher(
        epsilon: f64,
        delta: f64,
        buildhasher: B,
    ) -> CountMinSketch<C, B> {
        assert!(epsilon > 0., "epsilon must be greater than 0");
        assert!(
            (delta > 0.) & (delta < 1.),
            "delta ({}) must be greater than 0 and smaller than 1",
            delta
        );

        let w = (f64::consts::E / epsilon).ceil() as usize;
        let d = (1. / delta).ln().ceil() as usize;
        CountMinSketch::with_params_and_hasher(w, d, buildhasher)
    }

    /// Get number of columns of internal counter table.
    pub fn w(&self) -> usize {
        self.w
    }

    /// Get number of rows of internal counter table.
    pub fn d(&self) -> usize {
        self.d
    }

    /// Get `BuildHasher`
    pub fn buildhasher(&self) -> &B {
        &self.buildhasher
    }

    /// Check whether the CountMinSketch is empty (i.e. no elements seen yet).
    pub fn is_empty(&self) -> bool {
        self.table.iter().all(|x| x.is_zero())
    }

    /// Add one to the counter of the given element.
    pub fn add<T>(&mut self, obj: &T)
    where
        T: Hash,
    {
        self.add_n(&obj, C::one())
    }

    /// Add `n` to the counter of the given element.
    pub fn add_n<T>(&mut self, obj: &T, n: C)
    where
        T: Hash,
    {
        for (i, pos) in HashIter::new(self.w, self.d, obj, &self.buildhasher).enumerate() {
            let x = i * self.w + pos;
            self.table[x] = self.table[x].checked_add(&n).unwrap();
        }
    }

    /// Runs a point query, i.e. a query for the count of a single object.
    pub fn query_point<T>(&self, obj: &T) -> C
    where
        T: Hash,
    {
        HashIter::new(self.w, self.d, obj, &self.buildhasher)
            .enumerate()
            .map(|(i, pos)| i * self.w + pos)
            .map(|x| self.table[x].clone())
            .min()
            .unwrap()
    }

    /// Merge self with another CountMinSketch.
    ///
    /// After this operation `self` will be in the same state as when it would have seen all
    /// elements from `self` and `other`.
    ///
    /// Panics when `d`, `w` or `buildhasher` from `self` and `other` differ.
    pub fn merge(&mut self, other: &CountMinSketch<C, B>) {
        assert_eq!(
            self.d, other.d,
            "number of rows (d) must be equal (left={}, right={})",
            self.d, other.d
        );
        assert_eq!(
            self.w, other.w,
            "number of columns (w) must be equal (left={}, right={})",
            self.w, other.w
        );
        assert!(
            self.buildhasher == other.buildhasher,
            "buildhasher must be equal"
        );

        self.table = self.table
            .iter()
            .zip(other.table.iter())
            .map(|x| x.0.checked_add(x.1).unwrap())
            .collect();
    }

    /// Clear internal counters to a fresh state (i.e. no objects seen).
    pub fn clear(&mut self) {
        self.table = vec![C::zero(); self.w.checked_mul(self.d).unwrap()];
    }
}

impl fmt::Debug for CountMinSketch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "CountMinSketch {{ w: {}, d: {} }}", self.w, self.d)
    }
}

impl<T> Extend<T> for CountMinSketch
where
    T: Hash,
{
    fn extend<S: IntoIterator<Item = T>>(&mut self, iter: S) {
        for elem in iter {
            self.add(&elem);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::CountMinSketch;
    use hash_utils::BuildHasherSeeded;

    #[test]
    fn getter() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        assert_eq!(cms.w(), 10);
        assert_eq!(cms.d(), 20);
        cms.buildhasher();
    }

    #[test]
    fn properties() {
        let cms = CountMinSketch::<usize>::with_point_query_properties(0.01, 0.1);
        assert_eq!(cms.w(), 272);
        assert_eq!(cms.d(), 3);
    }

    #[test]
    #[should_panic(expected = "epsilon must be greater than 0")]
    fn properties_panics_epsilon0() {
        CountMinSketch::<usize>::with_point_query_properties(0., 0.1);
    }

    #[test]
    #[should_panic(expected = "delta (0) must be greater than 0 and smaller than 1")]
    fn properties_panics_delta0() {
        CountMinSketch::<usize>::with_point_query_properties(0.01, 0.);
    }

    #[test]
    #[should_panic(expected = "delta (1) must be greater than 0 and smaller than 1")]
    fn properties_panics_delta1() {
        CountMinSketch::<usize>::with_point_query_properties(0.01, 1.);
    }

    #[test]
    fn empty() {
        let cms = CountMinSketch::<usize>::with_params(10, 10);
        assert_eq!(cms.query_point(&1), 0);
        assert!(cms.is_empty());
    }

    #[test]
    fn add_1() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add(&1);
        assert_eq!(cms.query_point(&1), 1);
        assert_eq!(cms.query_point(&2), 0);
    }

    #[test]
    fn add_2() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add(&1);
        cms.add(&1);
        assert_eq!(cms.query_point(&1), 2);
        assert_eq!(cms.query_point(&2), 0);
    }

    #[test]
    fn add_2_1a() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add(&1);
        cms.add(&2);
        cms.add(&1);
        assert_eq!(cms.query_point(&1), 2);
        assert_eq!(cms.query_point(&2), 1);
        assert_eq!(cms.query_point(&3), 0);
    }

    #[test]
    fn add_2_1b() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add_n(&1, 2);
        cms.add(&2);
        assert_eq!(cms.query_point(&1), 2);
        assert_eq!(cms.query_point(&2), 1);
        assert_eq!(cms.query_point(&3), 0);
    }

    #[test]
    fn merge() {
        let mut cms1 = CountMinSketch::<usize>::with_params(10, 10);
        let mut cms2 = CountMinSketch::<usize>::with_params(10, 10);

        cms1.add_n(&1, 1);
        cms1.add_n(&2, 2);
        assert_eq!(cms1.query_point(&1), 1);
        assert_eq!(cms1.query_point(&2), 2);
        assert_eq!(cms1.query_point(&3), 0);
        assert_eq!(cms1.query_point(&4), 0);

        cms2.add_n(&2, 20);
        cms2.add_n(&3, 30);
        assert_eq!(cms2.query_point(&1), 0);
        assert_eq!(cms2.query_point(&2), 20);
        assert_eq!(cms2.query_point(&3), 30);
        assert_eq!(cms2.query_point(&4), 0);

        cms1.merge(&cms2);
        assert_eq!(cms1.query_point(&1), 1);
        assert_eq!(cms1.query_point(&2), 22);
        assert_eq!(cms1.query_point(&3), 30);
        assert_eq!(cms1.query_point(&4), 0);
    }

    #[test]
    #[should_panic(expected = "number of columns (w) must be equal (left=10, right=20)")]
    fn merge_panics_w() {
        let mut cms1 = CountMinSketch::<usize>::with_params(10, 10);
        let cms2 = CountMinSketch::<usize>::with_params(20, 10);
        cms1.merge(&cms2);
    }

    #[test]
    #[should_panic(expected = "number of rows (d) must be equal (left=10, right=20)")]
    fn merge_panics_d() {
        let mut cms1 = CountMinSketch::<usize>::with_params(10, 10);
        let cms2 = CountMinSketch::<usize>::with_params(10, 20);
        cms1.merge(&cms2);
    }

    #[test]
    #[should_panic(expected = "buildhasher must be equal")]
    fn merge_panics_buildhasher() {
        let mut cms1 = CountMinSketch::<usize, BuildHasherSeeded>::with_params_and_hasher(
            10,
            10,
            BuildHasherSeeded::new(0),
        );
        let cms2 = CountMinSketch::<usize, BuildHasherSeeded>::with_params_and_hasher(
            10,
            10,
            BuildHasherSeeded::new(1),
        );
        cms1.merge(&cms2);
    }

    #[test]
    fn clear() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add(&1);
        assert_eq!(cms.query_point(&1), 1);
        assert!(!cms.is_empty());

        cms.clear();
        assert_eq!(cms.query_point(&1), 0);
        assert!(cms.is_empty());
    }

    #[test]
    fn clone() {
        let mut cms1 = CountMinSketch::<usize>::with_params(10, 10);

        cms1.add(&1);
        assert_eq!(cms1.query_point(&1), 1);
        assert_eq!(cms1.query_point(&2), 0);

        let cms2 = cms1.clone();
        assert_eq!(cms2.query_point(&1), 1);
        assert_eq!(cms2.query_point(&2), 0);

        cms1.add(&1);
        assert_eq!(cms1.query_point(&1), 2);
        assert_eq!(cms1.query_point(&2), 0);
        assert_eq!(cms2.query_point(&1), 1);
        assert_eq!(cms2.query_point(&2), 0);
    }

    #[test]
    fn debug() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        assert_eq!(format!("{:?}", cms), "CountMinSketch { w: 10, d: 20 }");
    }

    #[test]
    fn extend() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.extend(vec![1, 1]);
        assert_eq!(cms.query_point(&1), 2);
        assert_eq!(cms.query_point(&2), 0);
    }
}
<commit_msg>countminsketch: add_n takes ref<commit_after>//! `CountMinSketch` implementation.
use std::collections::hash_map::DefaultHasher;
use std::f64;
use std::fmt;
use std::hash::{BuildHasher, Hash};

use num_traits::{CheckedAdd, One, Unsigned, Zero};

use hash_utils::{HashIter, MyBuildHasherDefault};

/// Simple implementation of a
/// [Count-min sketch](https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch)
///
/// The type parameter `C` sets the type of the counter in the internal table and can be used to
/// reduce memory consumption when low counts are expected.
#[derive(Clone)]
pub struct CountMinSketch<C = usize, B = MyBuildHasherDefault<DefaultHasher>>
where
    C: CheckedAdd + Clone + One + Ord + Unsigned + Zero,
    B: BuildHasher + Clone + Eq,
{
    table: Vec<C>,
    w: usize,
    d: usize,
    buildhasher: B,
}

impl<C> CountMinSketch<C>
where
    C: CheckedAdd + Clone + One + Ord + Unsigned + Zero,
{
    /// Create new CountMinSketch based on table size.
    ///
    /// - `w` sets the number of columns
    /// - `d` sets the number of rows
    pub fn with_params(w: usize, d: usize) -> CountMinSketch<C> {
        let bh = MyBuildHasherDefault::<DefaultHasher>::default();
        Self::with_params_and_hasher(w, d, bh)
    }

    /// Create new CountMinSketch with the following properties for a point query:
    ///
    /// - `a` is the real count of observed objects
    /// - `a'` is the guessed count of observed objects
    /// - `N` is the total count in the internal table
    /// - `a <= a'` always holds
    /// - `a' <= a + epsilon * N` holds with `p > 1 - delta`
    ///
    /// The following conditions must hold:
    ///
    /// - `epsilon > 0`
    /// - `delta > 0` and `delta < 1`
    ///
    /// Panics when the input conditions do not hold.
    pub fn with_point_query_properties(epsilon: f64, delta: f64) -> CountMinSketch<C> {
        let bh = MyBuildHasherDefault::<DefaultHasher>::default();
        Self::with_point_query_properties_and_hasher(epsilon, delta, bh)
    }
}

impl<C, B> CountMinSketch<C, B>
where
    C: CheckedAdd + Clone + One + Ord + Unsigned + Zero,
    B: BuildHasher + Clone + Eq,
{
    /// Same as `with_params` but with a specific `BuildHasher`.
    pub fn with_params_and_hasher(w: usize, d: usize, buildhasher: B) -> CountMinSketch<C, B> {
        let table = vec![C::zero(); w.checked_mul(d).unwrap()];
        CountMinSketch {
            table: table,
            w: w,
            d: d,
            buildhasher: buildhasher,
        }
    }

    /// Same as `with_params_and_hasher` but with a specific `BuildHasher`.
    pub fn with_point_query_properties_and_hasher(
        epsilon: f64,
        delta: f64,
        buildhasher: B,
    ) -> CountMinSketch<C, B> {
        assert!(epsilon > 0., "epsilon must be greater than 0");
        assert!(
            (delta > 0.) & (delta < 1.),
            "delta ({}) must be greater than 0 and smaller than 1",
            delta
        );

        let w = (f64::consts::E / epsilon).ceil() as usize;
        let d = (1. / delta).ln().ceil() as usize;
        CountMinSketch::with_params_and_hasher(w, d, buildhasher)
    }

    /// Get number of columns of internal counter table.
    pub fn w(&self) -> usize {
        self.w
    }

    /// Get number of rows of internal counter table.
    pub fn d(&self) -> usize {
        self.d
    }

    /// Get `BuildHasher`
    pub fn buildhasher(&self) -> &B {
        &self.buildhasher
    }

    /// Check whether the CountMinSketch is empty (i.e. no elements seen yet).
    pub fn is_empty(&self) -> bool {
        self.table.iter().all(|x| x.is_zero())
    }

    /// Add one to the counter of the given element.
    pub fn add<T>(&mut self, obj: &T)
    where
        T: Hash,
    {
        self.add_n(&obj, &C::one())
    }

    /// Add `n` to the counter of the given element.
    pub fn add_n<T>(&mut self, obj: &T, n: &C)
    where
        T: Hash,
    {
        for (i, pos) in HashIter::new(self.w, self.d, obj, &self.buildhasher).enumerate() {
            let x = i * self.w + pos;
            self.table[x] = self.table[x].checked_add(n).unwrap();
        }
    }

    /// Runs a point query, i.e. a query for the count of a single object.
    pub fn query_point<T>(&self, obj: &T) -> C
    where
        T: Hash,
    {
        HashIter::new(self.w, self.d, obj, &self.buildhasher)
            .enumerate()
            .map(|(i, pos)| i * self.w + pos)
            .map(|x| self.table[x].clone())
            .min()
            .unwrap()
    }

    /// Merge self with another CountMinSketch.
    ///
    /// After this operation `self` will be in the same state as when it would have seen all
    /// elements from `self` and `other`.
    ///
    /// Panics when `d`, `w` or `buildhasher` from `self` and `other` differ.
    pub fn merge(&mut self, other: &CountMinSketch<C, B>) {
        assert_eq!(
            self.d, other.d,
            "number of rows (d) must be equal (left={}, right={})",
            self.d, other.d
        );
        assert_eq!(
            self.w, other.w,
            "number of columns (w) must be equal (left={}, right={})",
            self.w, other.w
        );
        assert!(
            self.buildhasher == other.buildhasher,
            "buildhasher must be equal"
        );

        self.table = self.table
            .iter()
            .zip(other.table.iter())
            .map(|x| x.0.checked_add(x.1).unwrap())
            .collect();
    }

    /// Clear internal counters to a fresh state (i.e. no objects seen).
    pub fn clear(&mut self) {
        self.table = vec![C::zero(); self.w.checked_mul(self.d).unwrap()];
    }
}

impl fmt::Debug for CountMinSketch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "CountMinSketch {{ w: {}, d: {} }}", self.w, self.d)
    }
}

impl<T> Extend<T> for CountMinSketch
where
    T: Hash,
{
    fn extend<S: IntoIterator<Item = T>>(&mut self, iter: S) {
        for elem in iter {
            self.add(&elem);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::CountMinSketch;
    use hash_utils::BuildHasherSeeded;

    #[test]
    fn getter() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        assert_eq!(cms.w(), 10);
        assert_eq!(cms.d(), 20);
        cms.buildhasher();
    }

    #[test]
    fn properties() {
        let cms = CountMinSketch::<usize>::with_point_query_properties(0.01, 0.1);
        assert_eq!(cms.w(), 272);
        assert_eq!(cms.d(), 3);
    }

    #[test]
    #[should_panic(expected = "epsilon must be greater than 0")]
    fn properties_panics_epsilon0() {
        CountMinSketch::<usize>::with_point_query_properties(0., 0.1);
    }

    #[test]
    #[should_panic(expected = "delta (0) must be greater than 0 and smaller than 1")]
    fn properties_panics_delta0() {
        CountMinSketch::<usize>::with_point_query_properties(0.01, 0.);
    }

    #[test]
    #[should_panic(expected = "delta (1) must be greater than 0 and smaller than 1")]
    fn properties_panics_delta1() {
        CountMinSketch::<usize>::with_point_query_properties(0.01, 1.);
    }

    #[test]
    fn empty() {
        let cms = CountMinSketch::<usize>::with_params(10, 10);
        assert_eq!(cms.query_point(&1), 0);
        assert!(cms.is_empty());
    }

    #[test]
    fn add_1() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add(&1);
        assert_eq!(cms.query_point(&1), 1);
        assert_eq!(cms.query_point(&2), 0);
    }

    #[test]
    fn add_2() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add(&1);
        cms.add(&1);
        assert_eq!(cms.query_point(&1), 2);
        assert_eq!(cms.query_point(&2), 0);
    }

    #[test]
    fn add_2_1a() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add(&1);
        cms.add(&2);
        cms.add(&1);
        assert_eq!(cms.query_point(&1), 2);
        assert_eq!(cms.query_point(&2), 1);
        assert_eq!(cms.query_point(&3), 0);
    }

    #[test]
    fn add_2_1b() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add_n(&1, &2);
        cms.add(&2);
        assert_eq!(cms.query_point(&1), 2);
        assert_eq!(cms.query_point(&2), 1);
        assert_eq!(cms.query_point(&3), 0);
    }

    #[test]
    fn merge() {
        let mut cms1 = CountMinSketch::<usize>::with_params(10, 10);
        let mut cms2 = CountMinSketch::<usize>::with_params(10, 10);

        cms1.add_n(&1, &1);
        cms1.add_n(&2, &2);
        assert_eq!(cms1.query_point(&1), 1);
        assert_eq!(cms1.query_point(&2), 2);
        assert_eq!(cms1.query_point(&3), 0);
        assert_eq!(cms1.query_point(&4), 0);

        cms2.add_n(&2, &20);
        cms2.add_n(&3, &30);
        assert_eq!(cms2.query_point(&1), 0);
        assert_eq!(cms2.query_point(&2), 20);
        assert_eq!(cms2.query_point(&3), 30);
        assert_eq!(cms2.query_point(&4), 0);

        cms1.merge(&cms2);
        assert_eq!(cms1.query_point(&1), 1);
        assert_eq!(cms1.query_point(&2), 22);
        assert_eq!(cms1.query_point(&3), 30);
        assert_eq!(cms1.query_point(&4), 0);
    }

    #[test]
    #[should_panic(expected = "number of columns (w) must be equal (left=10, right=20)")]
    fn merge_panics_w() {
        let mut cms1 = CountMinSketch::<usize>::with_params(10, 10);
        let cms2 = CountMinSketch::<usize>::with_params(20, 10);
        cms1.merge(&cms2);
    }

    #[test]
    #[should_panic(expected = "number of rows (d) must be equal (left=10, right=20)")]
    fn merge_panics_d() {
        let mut cms1 = CountMinSketch::<usize>::with_params(10, 10);
        let cms2 = CountMinSketch::<usize>::with_params(10, 20);
        cms1.merge(&cms2);
    }

    #[test]
    #[should_panic(expected = "buildhasher must be equal")]
    fn merge_panics_buildhasher() {
        let mut cms1 = CountMinSketch::<usize, BuildHasherSeeded>::with_params_and_hasher(
            10,
            10,
            BuildHasherSeeded::new(0),
        );
        let cms2 = CountMinSketch::<usize, BuildHasherSeeded>::with_params_and_hasher(
            10,
            10,
            BuildHasherSeeded::new(1),
        );
        cms1.merge(&cms2);
    }

    #[test]
    fn clear() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.add(&1);
        assert_eq!(cms.query_point(&1), 1);
        assert!(!cms.is_empty());

        cms.clear();
        assert_eq!(cms.query_point(&1), 0);
        assert!(cms.is_empty());
    }

    #[test]
    fn clone() {
        let mut cms1 = CountMinSketch::<usize>::with_params(10, 10);

        cms1.add(&1);
        assert_eq!(cms1.query_point(&1), 1);
        assert_eq!(cms1.query_point(&2), 0);

        let cms2 = cms1.clone();
        assert_eq!(cms2.query_point(&1), 1);
        assert_eq!(cms2.query_point(&2), 0);

        cms1.add(&1);
        assert_eq!(cms1.query_point(&1), 2);
        assert_eq!(cms1.query_point(&2), 0);
        assert_eq!(cms2.query_point(&1), 1);
        assert_eq!(cms2.query_point(&2), 0);
    }

    #[test]
    fn debug() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        assert_eq!(format!("{:?}", cms), "CountMinSketch { w: 10, d: 20 }");
    }

    #[test]
    fn extend() {
        let mut cms = CountMinSketch::<usize>::with_params(10, 10);

        cms.extend(vec![1, 1]);
        assert_eq!(cms.query_point(&1), 2);
        assert_eq!(cms.query_point(&2), 0);
    }
}
<|endoftext|>


<commit_before>extern crate csv;

use std::io;
use std::path::Path;
use std::num;
use cpu::common_defs::OpcodeExecInfo;
use cpu::common_defs::OpcodeDebugInfo;
use cpu::common_defs::opcode_class;
use cpu::common_defs::address_mode;

#[derive(Debug)]
pub enum OpcodeLoadError {
  Io(io::Error),
  CSV(csv::Error),
  ParseInt(num::ParseIntError),
  ParseOpcodeClass(opcode_class::ParseError),
  ParseAddressMode(address_mode::ParseError),
  DuplicateOpcode(String),
  IncorrectOpcodeCount(String)
}

impl From<csv::Error> for OpcodeLoadError {
    fn from(err:csv::Error) -> OpcodeLoadError {
        OpcodeLoadError::CSV(err)
    }
}
impl From<num::ParseIntError> for OpcodeLoadError {
    fn from(err:num::ParseIntError) -> OpcodeLoadError {
        OpcodeLoadError::ParseInt(err)
    }
}
impl From<opcode_class::ParseError> for OpcodeLoadError {
    fn from(err:opcode_class::ParseError) -> OpcodeLoadError {
        OpcodeLoadError::ParseOpcodeClass(err)
    }
}
impl From<address_mode::ParseError> for OpcodeLoadError {
    fn from(err:address_mode::ParseError) -> OpcodeLoadError {
        OpcodeLoadError::ParseAddressMode(err)
    }
}

pub fn load_from_file<P:AsRef<Path>>(file_path: P) -> Result<(Vec<OpcodeExecInfo>,Vec<OpcodeDebugInfo>),OpcodeLoadError> {
    let mut rdr = try!(csv::Reader::from_file(file_path));
    rdr = rdr.has_headers(true);
    rdr = rdr.flexible(false); // all records are the same length

    let expected_opcode_count = 256;
    let mut exec_info_vec = Vec::with_capacity(expected_opcode_count);
    let mut debug_info_vec = Vec::with_capacity(expected_opcode_count);
    let mut duplicate_check = Vec::<u8>::new();

    let mut count = 0;

    for rec in rdr.decode() {
        let (opcode_string,name,address_mode_name,len,cycles,page_cycles,notes) : (String,String,String,u8,u8,u8,String) = rec.unwrap();
        let opcode = try!(u8::from_str_radix(&opcode_string[2..],16)); // from_str_radix won't parse 0x

        if duplicate_check.contains(&opcode) {
            return Err(OpcodeLoadError::DuplicateOpcode(format!("{:X}",opcode)));
        }
        duplicate_check.push(opcode);

        let debug_info = OpcodeDebugInfo { opcode : opcode, name : name.trim().to_string(), address_mode_name : address_mode_name.trim().to_string(), notes : notes, };

        let address_mode = try!(debug_info.address_mode_name.parse::<address_mode::AddressMode>());
        let opcode_class = try!(name.trim().to_string().parse::<opcode_class::OpcodeClass>());

        let exec_info = OpcodeExecInfo { opcode: opcode, len: len, cycles: cycles, page_cycles: page_cycles, address_mode: address_mode,opcode_class:opcode_class };

        debug_info_vec.push(debug_info);
        exec_info_vec.push(exec_info);

        count = count+1;
    }
    if exec_info_vec.len() != expected_opcode_count {
        return Err(OpcodeLoadError::IncorrectOpcodeCount(format!("Expected {} opcodes, found {}",expected_opcode_count,exec_info_vec.len())));
    }

    exec_info_vec.sort_by(|a,b| u8::cmp(&a.opcode,&b.opcode));
    debug_info_vec.sort_by(|a,b| u8::cmp(&a.opcode,&b.opcode));
    Ok((exec_info_vec,debug_info_vec))
}

<commit_msg>opcode load now checks that opcodes are contiguous<commit_after>extern crate csv;

use std::io;
use std::path::Path;
use std::num;
use cpu::common_defs::OpcodeExecInfo;
use cpu::common_defs::OpcodeDebugInfo;
use cpu::common_defs::opcode_class;
use cpu::common_defs::address_mode;

#[derive(Debug)]
pub enum OpcodeLoadError {
    Io(io::Error),
    CSV(csv::Error),
    ParseInt(num::ParseIntError),
    ParseOpcodeClass(opcode_class::ParseError),
    ParseAddressMode(address_mode::ParseError),
    DuplicateOpcode(String),
    IncorrectOpcodeCount(String),
    NonContiguousOpcodes(String)
}

impl From<csv::Error> for OpcodeLoadError {
    fn from(err:csv::Error) -> OpcodeLoadError {
        OpcodeLoadError::CSV(err)
    }
}
impl From<num::ParseIntError> for OpcodeLoadError {
    fn from(err:num::ParseIntError) -> OpcodeLoadError {
        OpcodeLoadError::ParseInt(err)
    }
}
impl From<opcode_class::ParseError> for OpcodeLoadError {
    fn from(err:opcode_class::ParseError) -> OpcodeLoadError {
        OpcodeLoadError::ParseOpcodeClass(err)
    }
}
impl From<address_mode::ParseError> for OpcodeLoadError {
    fn from(err:address_mode::ParseError) -> OpcodeLoadError {
        OpcodeLoadError::ParseAddressMode(err)
    }
}

pub fn load_from_file<P:AsRef<Path>>(file_path: P) -> Result<(Vec<OpcodeExecInfo>,Vec<OpcodeDebugInfo>),OpcodeLoadError> {
    let mut rdr = try!(csv::Reader::from_file(file_path));
    rdr = rdr.has_headers(true);
    rdr = rdr.flexible(false); // all records are the same length

    let expected_opcode_count = 256;
    let mut exec_info_vec = Vec::with_capacity(expected_opcode_count);
    let mut debug_info_vec = Vec::with_capacity(expected_opcode_count);
    let mut duplicate_check = Vec::<u8>::new();

    let mut count = 0;

    for rec in rdr.decode() {
        let (opcode_string,name,address_mode_name,len,cycles,page_cycles,notes) : (String,String,String,u8,u8,u8,String) = rec.unwrap();
        let opcode = try!(u8::from_str_radix(&opcode_string[2..],16)); // from_str_radix won't parse 0x

        if duplicate_check.contains(&opcode) {
            return Err(OpcodeLoadError::DuplicateOpcode(format!("{:X}",opcode)));
        }
        duplicate_check.push(opcode);

        let debug_info = OpcodeDebugInfo { opcode : opcode, name : name.trim().to_string(), address_mode_name : address_mode_name.trim().to_string(), notes : notes, };

        let address_mode = try!(debug_info.address_mode_name.parse::<address_mode::AddressMode>());
        let opcode_class = try!(name.trim().to_string().parse::<opcode_class::OpcodeClass>());

        let exec_info = OpcodeExecInfo { opcode: opcode, len: len, cycles: cycles, page_cycles: page_cycles, address_mode: address_mode,opcode_class:opcode_class };

        debug_info_vec.push(debug_info);
        exec_info_vec.push(exec_info);

        count = count+1;
    }
    if exec_info_vec.len() != expected_opcode_count {
        return Err(OpcodeLoadError::IncorrectOpcodeCount(format!("Expected {} opcodes, found {}",expected_opcode_count,exec_info_vec.len())));
    }

    exec_info_vec.sort_by(|a,b| u8::cmp(&a.opcode,&b.opcode));
    debug_info_vec.sort_by(|a,b| u8::cmp(&a.opcode,&b.opcode));

    for (i,op) in exec_info_vec.iter().enumerate() {
        if i != op.opcode as usize {
            return Err(OpcodeLoadError::NonContiguousOpcodes(format!("opcodes must be contiguous from 0 to 255, found gap at opcode {:X}",op.opcode)));
        }
    }
    Ok((exec_info_vec,debug_info_vec))
}

<|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).

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

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
];

type DrawCallback<'a> = |pixels: &[u8]|: 'a;

/// CHIP-8 virtual machine
pub struct Chip8<'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>
}

impl <'a> Chip8 <'a> {

    /// Create a new Chip8 VM
    ///
    /// ## Arguments ##
    /// * draw_callback - Callback used when drawing
    pub fn new(draw_callback: DrawCallback<'a>) -> Chip8<'a> {
        let mut ch8 = Chip8 {
            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
        };
        copy_memory(ch8.ram.mut_slice(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.mut_slice(START_ADDR as uint, len), rom);
    }

    /// Do an emulation cycle.
    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x0000 => match ins & 0x0FFF {
                0x00E0 => self.clear_display(),
                0x00EE => self.ret_from_subroutine(),
                _ => self.jump_to_sys_routine(0)
            },
            0x2000 => self.call_subroutine((ins & 0xFFF) as uint),
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x000A => self.wait_for_keypress_store_in_vx(((ins & 0x0F00) >> 8) as uint),
                0x0029 => self.set_i_to_loc_of_digit_vx(((ins & 0x0F00) >> 8) as uint),
                0x0033 => self.store_bcd_of_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:04x}", ins)
        }
    }

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

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }

        (self.draw_callback)(self.display);
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, self.i as uint + x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, self.i as uint + x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }

    fn clear_display(&mut self) {
        for px in self.display.mut_iter() {
            *px = 0;
        }
    }

    fn jump_to_sys_routine(&mut self, addr: uint) {
        unimplemented!();
    }

    fn call_subroutine(&mut self, addr: uint) {
        self.sp += 1;
        self.stack[self.sp as uint] = self.pc;
        self.pc = addr as u16;
    }

    // Fx33 - LD B, Vx
    // Store BCD representation of Vx in memory locations I, I+1, and I+2.
    //
    // The interpreter takes the decimal value of Vx, and places the hundreds
    // digit in memory at location in I, the tens digit at location I+1,
    // and the ones digit at location I+2.
    fn store_bcd_of_vx_to_i(&mut self, x: uint) {
        let num = self.v[x];
        let h = num / 100;
        let t = (num - h * 100) / 10;
        let o = (num - h * 100 - t * 10);
        self.ram[self.i as uint] = h;
        self.ram[self.i as uint + 1] = t;
        self.ram[self.i as uint + 2] = o;
    }

    // Fx29 - LD F, Vx
    // Set I = location of sprite for digit Vx.
    //
    // The value of I is set to the location for the hexadecimal sprite
    // corresponding to the value of Vx. See section 2.4, Display, for more
    // information on the Chip-8 hexadecimal font.
    //
    // For crusty-chip, the fontset is stored at 0x000
    fn set_i_to_loc_of_digit_vx(&mut self, x: uint) {
        self.i = (self.v[x] * 5) as u16;
    }

    // 00EE - RET
    // Return from a subroutine.
    //
    // The interpreter sets the program counter to the address at the top of
    // the stack, then subtracts 1 from the stack pointer.
    fn ret_from_subroutine(&mut self) {
        self.pc = self.stack[self.sp as uint];
        self.sp -= 1;
    }

    // Fx0A - LD Vx, K
    // Wait for a key press, store the value of the key in Vx.
    //
    // All execution stops until a key is pressed, then the value of that key
    //  is stored in Vx.
    fn wait_for_keypress_store_in_vx(&mut self, x: uint) {
        self.pc -= 2;
    }
}

#[test]
fn test_strore_bcd_of_vx_to_i() {
    let mut ch8 = Chip8::new(|_| {});
    ch8.v[0] = 146;
    ch8.i = 0;
    ch8.store_bcd_of_vx_to_i(0);
    assert!(ch8.ram[0] == 1);
    assert!(ch8.ram[1] == 4);
    assert!(ch8.ram[2] == 6);
}
<commit_msg>Document DrawCallback<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).

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

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 crustychip::{ DISPLAY_WIDTH, Chip8 };
///     // 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 = Chip8::new(dump_pixels);
///     // ...
/// ```
pub type DrawCallback<'a> = |pixels: &[u8]|: 'a;

/// CHIP-8 virtual machine
pub struct Chip8<'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>
}

impl <'a> Chip8 <'a> {

    /// Create a new Chip8 VM
    ///
    /// ## Arguments ##
    /// * draw_callback - Callback used when drawing
    pub fn new(draw_callback: DrawCallback<'a>) -> Chip8<'a> {
        let mut ch8 = Chip8 {
            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
        };
        copy_memory(ch8.ram.mut_slice(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.mut_slice(START_ADDR as uint, len), rom);
    }

    /// Do an emulation cycle.
    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x0000 => match ins & 0x0FFF {
                0x00E0 => self.clear_display(),
                0x00EE => self.ret_from_subroutine(),
                _ => self.jump_to_sys_routine(0)
            },
            0x2000 => self.call_subroutine((ins & 0xFFF) as uint),
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x000A => self.wait_for_keypress_store_in_vx(((ins & 0x0F00) >> 8) as uint),
                0x0029 => self.set_i_to_loc_of_digit_vx(((ins & 0x0F00) >> 8) as uint),
                0x0033 => self.store_bcd_of_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:04x}", ins)
        }
    }

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

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }

        (self.draw_callback)(self.display);
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, self.i as uint + x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, self.i as uint + x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }

    fn clear_display(&mut self) {
        for px in self.display.mut_iter() {
            *px = 0;
        }
    }

    fn jump_to_sys_routine(&mut self, addr: uint) {
        unimplemented!();
    }

    fn call_subroutine(&mut self, addr: uint) {
        self.sp += 1;
        self.stack[self.sp as uint] = self.pc;
        self.pc = addr as u16;
    }

    // Fx33 - LD B, Vx
    // Store BCD representation of Vx in memory locations I, I+1, and I+2.
    //
    // The interpreter takes the decimal value of Vx, and places the hundreds
    // digit in memory at location in I, the tens digit at location I+1,
    // and the ones digit at location I+2.
    fn store_bcd_of_vx_to_i(&mut self, x: uint) {
        let num = self.v[x];
        let h = num / 100;
        let t = (num - h * 100) / 10;
        let o = (num - h * 100 - t * 10);
        self.ram[self.i as uint] = h;
        self.ram[self.i as uint + 1] = t;
        self.ram[self.i as uint + 2] = o;
    }

    // Fx29 - LD F, Vx
    // Set I = location of sprite for digit Vx.
    //
    // The value of I is set to the location for the hexadecimal sprite
    // corresponding to the value of Vx. See section 2.4, Display, for more
    // information on the Chip-8 hexadecimal font.
    //
    // For crusty-chip, the fontset is stored at 0x000
    fn set_i_to_loc_of_digit_vx(&mut self, x: uint) {
        self.i = (self.v[x] * 5) as u16;
    }

    // 00EE - RET
    // Return from a subroutine.
    //
    // The interpreter sets the program counter to the address at the top of
    // the stack, then subtracts 1 from the stack pointer.
    fn ret_from_subroutine(&mut self) {
        self.pc = self.stack[self.sp as uint];
        self.sp -= 1;
    }

    // Fx0A - LD Vx, K
    // Wait for a key press, store the value of the key in Vx.
    //
    // All execution stops until a key is pressed, then the value of that key
    //  is stored in Vx.
    fn wait_for_keypress_store_in_vx(&mut self, x: uint) {
        self.pc -= 2;
    }
}

#[test]
fn test_strore_bcd_of_vx_to_i() {
    let mut ch8 = Chip8::new(|_| {});
    ch8.v[0] = 146;
    ch8.i = 0;
    ch8.store_bcd_of_vx_to_i(0);
    assert!(ch8.ram[0] == 1);
    assert!(ch8.ram[1] == 4);
    assert!(ch8.ram[2] == 6);
}
<|endoftext|>