<commit_before>use super::memory;

const REG_PAIR_BC:u8 = 0b00;
const REG_PAIR_DE:u8 = 0b01;
const REG_PAIR_HL:u8 = 0b10;
const REG_PAIR_SP:u8 = 0b11;

#[derive(Debug)]
pub struct Cpu {
    af: u16,
    bc: u16,
    de: u16,
    hl: u16,
    af_: u16,
    bc_: u16,
    de_: u16,
    hl_: u16,
    ir: u16,
    ix: u16,
    iy: u16,
    sp: u16,
    pc: u16,

    iff1: bool,
    iff2: bool,

    memory: memory::Memory
}

impl Cpu {
    pub fn new(memory: memory::Memory) -> Cpu {
        Cpu {
            af: 0,
            bc: 0,
            de: 0,
            hl: 0,
            af_: 0,
            bc_: 0,
            de_: 0,
            hl_: 0,
            ir: 0,
            ix: 0,
            iy: 0,
            sp: 0,
            pc: 0,

            iff1: false,
            iff2: false,

            memory: memory
        }
    }

    pub fn run(&mut self) {
        loop {
            self.run_instruction();
        }
    }

    fn run_instruction(&mut self) {
        let instruction = self.read_word(self.pc);

        match instruction {
            0b11110011 => {
                self.iff1 = false;
                self.iff1 = false;
                println!("{:#x}: DI", self.pc);
                self.pc += 1;
            },
            0b00000001 | 0b00010001 | 0b00100001 | 0b00110001 => {
                let nn =
                    (self.read_word(self.pc + 1) as u16) +
                    ((self.read_word(self.pc + 2) as u16) << 8);
                match (instruction & 0b00110000) >> 4 {
                    REG_PAIR_BC => {
                        self.bc = nn;
                        println!("{:#x}: LD BC, ${:x}", self.pc, nn);
                    }
                    REG_PAIR_DE => {
                        self.de = nn;
                        println!("{:#x}: LD DE, ${:x}", self.pc, nn);
                    }
                    REG_PAIR_HL => {
                        self.hl = nn;
                        println!("{:#x}: LD HL, ${:x}", self.pc, nn);
                    }
                    REG_PAIR_SP => {
                        self.sp = nn;
                        println!("{:#x}: LD SP, ${:x}", self.pc, nn);
                    }
                    _ => {
                        panic!("Error when parsing \"LD dd, nn\" instruction");
                    }
                }
                self.pc += 3;
            },
            0b00001011 | 0b00011011 | 0b00101011 | 0b00111011 => {
                match (instruction & 0b00110000) >> 4 {
                    REG_PAIR_BC => {
                        self.bc -= 1;
                        println!("{:#x}: DEC BC", self.pc);
                    }
                    REG_PAIR_DE => {
                        self.de -= 1;
                        println!("{:#x}: DEC DE", self.pc);
                    }
                    REG_PAIR_HL => {
                        self.hl -= 1;
                        println!("{:#x}: DEC HL", self.pc);
                    }
                    REG_PAIR_SP => {
                        self.sp -= 1;
                        println!("{:#x}: DEC SP", self.pc);
                    }
                    _ => {
                        panic!("Error when parsing \"DEC ss\" instruction");
                    }
                }
                self.pc += 1;
            }
            _ => {
                panic!("Unrecognized instruction: {:#x}", instruction);
            }
        }

    }

    fn read_word(&self, addr: u16) -> u8 {
        self.memory.read_word(addr)
    }
}

<commit_msg>Fix DI instruction bug<commit_after>use super::memory;

const REG_PAIR_BC:u8 = 0b00;
const REG_PAIR_DE:u8 = 0b01;
const REG_PAIR_HL:u8 = 0b10;
const REG_PAIR_SP:u8 = 0b11;

#[derive(Debug)]
pub struct Cpu {
    af: u16,
    bc: u16,
    de: u16,
    hl: u16,
    af_: u16,
    bc_: u16,
    de_: u16,
    hl_: u16,
    ir: u16,
    ix: u16,
    iy: u16,
    sp: u16,
    pc: u16,

    iff1: bool,
    iff2: bool,

    memory: memory::Memory
}

impl Cpu {
    pub fn new(memory: memory::Memory) -> Cpu {
        Cpu {
            af: 0,
            bc: 0,
            de: 0,
            hl: 0,
            af_: 0,
            bc_: 0,
            de_: 0,
            hl_: 0,
            ir: 0,
            ix: 0,
            iy: 0,
            sp: 0,
            pc: 0,

            iff1: false,
            iff2: false,

            memory: memory
        }
    }

    pub fn run(&mut self) {
        loop {
            self.run_instruction();
        }
    }

    fn run_instruction(&mut self) {
        let instruction = self.read_word(self.pc);

        match instruction {
            0b11110011 => {
                self.iff1 = false;
                self.iff2 = false;
                println!("{:#x}: DI", self.pc);
                self.pc += 1;
            },
            0b00000001 | 0b00010001 | 0b00100001 | 0b00110001 => {
                let nn =
                    (self.read_word(self.pc + 1) as u16) +
                    ((self.read_word(self.pc + 2) as u16) << 8);
                match (instruction & 0b00110000) >> 4 {
                    REG_PAIR_BC => {
                        self.bc = nn;
                        println!("{:#x}: LD BC, ${:x}", self.pc, nn);
                    }
                    REG_PAIR_DE => {
                        self.de = nn;
                        println!("{:#x}: LD DE, ${:x}", self.pc, nn);
                    }
                    REG_PAIR_HL => {
                        self.hl = nn;
                        println!("{:#x}: LD HL, ${:x}", self.pc, nn);
                    }
                    REG_PAIR_SP => {
                        self.sp = nn;
                        println!("{:#x}: LD SP, ${:x}", self.pc, nn);
                    }
                    _ => {
                        panic!("Error when parsing \"LD dd, nn\" instruction");
                    }
                }
                self.pc += 3;
            },
            0b00001011 | 0b00011011 | 0b00101011 | 0b00111011 => {
                match (instruction & 0b00110000) >> 4 {
                    REG_PAIR_BC => {
                        self.bc -= 1;
                        println!("{:#x}: DEC BC", self.pc);
                    }
                    REG_PAIR_DE => {
                        self.de -= 1;
                        println!("{:#x}: DEC DE", self.pc);
                    }
                    REG_PAIR_HL => {
                        self.hl -= 1;
                        println!("{:#x}: DEC HL", self.pc);
                    }
                    REG_PAIR_SP => {
                        self.sp -= 1;
                        println!("{:#x}: DEC SP", self.pc);
                    }
                    _ => {
                        panic!("Error when parsing \"DEC ss\" instruction");
                    }
                }
                self.pc += 1;
            }
            _ => {
                panic!("Unrecognized instruction: {:#x}", instruction);
            }
        }

    }

    fn read_word(&self, addr: u16) -> u8 {
        self.memory.read_word(addr)
    }
}

<|endoftext|>


<commit_before>use parse::Executable;
use instruction::{Instruction, Condition, Operand, Port};

struct Cpu {
    pc: i32,
    pub acc: i32,
    pub bak: i32,
    executable: Executable
}

impl Cpu {
    pub fn new(executable: Executable) -> Cpu {
        Cpu { pc: 0,
              acc: 0,
              bak: 0,
              executable: executable,
        }
    }

    pub fn execute(&mut self) -> bool {
        if self.executable.len() == 0 {
            return false;
        }

        let advance_pc = match *self.executable.insn_at(self.pc()) {
            Instruction::NOP => true,
            Instruction::MOV { ref src, ref dst } => {
                match self.get_operand(src) {
                    Some(i) => match dst {
                            &Operand::Lit(_) => panic!("Cannot store to a literal"),
                            &Operand::ACC => { self.acc = i; true },
                            &Operand::Port(ref p) => self.write_port(p.to_owned(), i),
                    },
                    None => false
                }
            },
            Instruction::SWP => {
                let tmp = self.acc;
                self.acc = self.bak;
                self.bak = tmp;
                true
            },
            Instruction::SAV => {
                self.bak = self.acc;
                true
            },
            Instruction::ADD { ref addend } => {
                match self.get_operand(addend) {
                    Some(i) => { self.acc += i; true },
                    None => false
                }
            },
            Instruction::SUB { ref subtrahend } => {
                match self.get_operand(subtrahend) {
                    Some(i) => { self.acc -= i; true },
                    None => false
                }
            },
            Instruction::NEG => {
                self.acc = -self.acc;
                true
            },
            Instruction::J { ref cond, ref dst } => {
                if match *cond {
                    Condition::Unconditional => true,
                    Condition::Ez => self.acc == 0,
                    Condition::Nz => self.acc != 0,
                    Condition::Gz => self.acc > 0,
                    Condition::Lz => self.acc < 0,
                } {
                    self.pc = self.executable.label_line(dst) as i32;
                    false
                } else {
                    true
                }
            },
            Instruction::JRO { ref dst } => {
                match self.get_operand(dst) {
                    Some(i) => { self.pc += i; true },
                    None => false
                }
            },
        };
        if advance_pc {
            self.pc += 1;
        }
        /* Handle wrapping at the end and via JRO */
        self.pc %= self.executable.len() as i32;
        if self.pc < 0 {
            self.pc = self.executable.len() as i32 + self.pc;
        }
        true
    }

    pub fn current_line(&self) -> u32 {
        self.executable.srcline_at(self.pc())
    }

    fn read_port(&self, port: Port) -> Option<i32> {
        panic!("Unimplemented port read");
    }

    fn write_port(&self, port: Port, val: i32) -> bool {
        panic!("Unimplemented port write");
    }

    fn get_operand(&self, op: &Operand) -> Option<i32> {
        match op {
            &Operand::Lit(i) => Some(i),
            &Operand::ACC => Some(self.acc),
            &Operand::Port(ref p) => self.read_port(p.to_owned()),
        }
    }

    fn pc(&self) -> usize {
        self.pc as usize
    }
}

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

    #[test]
    fn test_cpu_wrapping() {
        let e = parse::parse("TOP: NOP\nNOP").unwrap();
        let mut cpu = Cpu::new(e);
        assert_eq!(cpu.current_line(), 0);
        cpu.execute();
        assert_eq!(cpu.current_line(), 1);
        cpu.execute();
        assert_eq!(cpu.current_line(), 0);
        cpu.execute();
        assert_eq!(cpu.current_line(), 1);
        cpu.execute();
    }

    #[test]
    fn test_mov() {
        let e = parse::parse("MOV 10 ACC\nNOP").unwrap();
        let mut cpu = Cpu::new(e);
        assert_eq!(cpu.current_line(), 0);
        assert_eq!(cpu.acc, 0);
        cpu.execute();
        assert_eq!(cpu.current_line(), 1);
        assert_eq!(cpu.acc, 10);
    }

    #[test]
    fn test_add_sub() {
        let e = parse::parse("ADD 10\nADD -20\nSUB 10\nSUB -30").unwrap();
        let mut cpu = Cpu::new(e);
        assert_eq!(cpu.current_line(), 0);
        assert_eq!(cpu.acc, 0);
        cpu.execute();
        assert_eq!(cpu.current_line(), 1);
        assert_eq!(cpu.acc, 10);
        cpu.execute();
        assert_eq!(cpu.current_line(), 2);
        assert_eq!(cpu.acc, -10);
        cpu.execute();
        assert_eq!(cpu.current_line(), 3);
        assert_eq!(cpu.acc, -20);
        cpu.execute();
        assert_eq!(cpu.current_line(), 0);
        assert_eq!(cpu.acc, 10);
    }
}
<commit_msg>cpu: move state to separate struct<commit_after>use parse::Executable;
use instruction::{Instruction, Condition, Operand, Port};

#[derive(Default)]
pub struct CpuState {
    pub acc: i32,
    pub bak: i32,
    pc: i32,
}

pub struct Cpu {
    pub state: CpuState,
    executable: Executable,
}

impl Cpu {
    pub fn new(executable: Executable) -> Cpu {
        Cpu { state: Default::default(),
              executable: executable,
        }
    }

    pub fn execute(&mut self) -> bool {
        if self.executable.len() == 0 {
            return false;
        }

        let advance_pc = match *self.executable.insn_at(self.pc()) {
            Instruction::NOP => true,
            Instruction::MOV { ref src, ref dst } => {
                match self.get_operand(src) {
                    Some(i) => match dst {
                            &Operand::Lit(_) => panic!("Cannot store to a literal"),
                            &Operand::ACC => { self.state.acc = i; true },
                            &Operand::Port(ref p) => self.write_port(p.to_owned(), i),
                    },
                    None => false
                }
            },
            Instruction::SWP => {
                let tmp = self.state.acc;
                self.state.acc = self.state.bak;
                self.state.bak = tmp;
                true
            },
            Instruction::SAV => {
                self.state.bak = self.state.acc;
                true
            },
            Instruction::ADD { ref addend } => {
                match self.get_operand(addend) {
                    Some(i) => { self.state.acc += i; true },
                    None => false
                }
            },
            Instruction::SUB { ref subtrahend } => {
                match self.get_operand(subtrahend) {
                    Some(i) => { self.state.acc -= i; true },
                    None => false
                }
            },
            Instruction::NEG => {
                self.state.acc = -self.state.acc;
                true
            },
            Instruction::J { ref cond, ref dst } => {
                if match *cond {
                    Condition::Unconditional => true,
                    Condition::Ez => self.state.acc == 0,
                    Condition::Nz => self.state.acc != 0,
                    Condition::Gz => self.state.acc > 0,
                    Condition::Lz => self.state.acc < 0,
                } {
                    self.state.pc = self.executable.label_line(dst) as i32;
                    false
                } else {
                    true
                }
            },
            Instruction::JRO { ref dst } => {
                match self.get_operand(dst) {
                    Some(i) => { self.state.pc += i; true },
                    None => false
                }
            },
        };
        if advance_pc {
            self.state.pc += 1;
        }
        /* Handle wrapping at the end and via JRO */
        self.state.pc %= self.executable.len() as i32;
        if self.state.pc < 0 {
            self.state.pc = self.executable.len() as i32 + self.state.pc;
        }
        true
    }

    pub fn current_line(&self) -> u32 {
        self.executable.srcline_at(self.pc())
    }

    fn read_port(&self, port: Port) -> Option<i32> {
        panic!("Unimplemented port read");
    }

    fn write_port(&self, port: Port, val: i32) -> bool {
        panic!("Unimplemented port write");
    }

    fn get_operand(&self, op: &Operand) -> Option<i32> {
        match op {
            &Operand::Lit(i) => Some(i),
            &Operand::ACC => Some(self.state.acc),
            &Operand::Port(ref p) => self.read_port(p.to_owned()),
        }
    }

    fn pc(&self) -> usize {
        self.state.pc as usize
    }
}

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

    #[test]
    fn test_cpu_wrapping() {
        let e = parse::parse("TOP: NOP\nNOP").unwrap();
        let mut cpu = Cpu::new(e);
        assert_eq!(cpu.current_line(), 0);
        cpu.execute();
        assert_eq!(cpu.current_line(), 1);
        cpu.execute();
        assert_eq!(cpu.current_line(), 0);
        cpu.execute();
        assert_eq!(cpu.current_line(), 1);
        cpu.execute();
    }

    #[test]
    fn test_mov() {
        let e = parse::parse("MOV 10 ACC\nNOP").unwrap();
        let mut cpu = Cpu::new(e);
        assert_eq!(cpu.current_line(), 0);
        assert_eq!(cpu.state.acc, 0);
        cpu.execute();
        assert_eq!(cpu.current_line(), 1);
        assert_eq!(cpu.state.acc, 10);
    }

    #[test]
    fn test_add_sub() {
        let e = parse::parse("ADD 10\nADD -20\nSUB 10\nSUB -30").unwrap();
        let mut cpu = Cpu::new(e);
        assert_eq!(cpu.current_line(), 0);
        assert_eq!(cpu.state.acc, 0);
        cpu.execute();
        assert_eq!(cpu.current_line(), 1);
        assert_eq!(cpu.state.acc, 10);
        cpu.execute();
        assert_eq!(cpu.current_line(), 2);
        assert_eq!(cpu.state.acc, -10);
        cpu.execute();
        assert_eq!(cpu.current_line(), 3);
        assert_eq!(cpu.state.acc, -20);
        cpu.execute();
        assert_eq!(cpu.current_line(), 0);
        assert_eq!(cpu.state.acc, 10);
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::rc::Rc;
use std::fmt;
use env;

#[derive(Clone, PartialEq, Show)]
pub enum Expr {
    Int(isize),
    Str(String),
    Sym(String),
    Bool(bool),
    Char(char),
    Pair(Vec<Expr>),
    Proc(ProcFunc),
    CompProc(Vec<Expr>, Rc<RefCell<env::Env>>),
    Nil
}

#[derive(Clone)]
pub struct ProcFunc(fn(Expr) -> Expr);

impl PartialEq for ProcFunc {
    fn eq(&self, o: &ProcFunc) -> bool {
        let _o: *const() = unsafe { ::std::mem::transmute(o)};
        let _s: *const() = unsafe { ::std::mem::transmute(self)};
        _s == _o
    }
    fn ne(&self, o: &ProcFunc) -> bool {
        !self.eq(o)
    }
}

impl ProcFunc {
    pub fn func(&self) -> (fn(Expr) -> Expr) {
        match *self {
            ProcFunc(fun) => fun
        }
    }
}

impl fmt::Show for ProcFunc {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "proc")
    }
}

impl Expr {
    pub fn new_pair(car: Expr, cdr: Expr) -> Expr {
        Expr::Pair(vec![car, cdr])
    }

    pub fn new_str(val: &str) -> Expr {
        Expr::Str(val.to_string())
    }

    pub fn new_sym(val: &str) -> Expr {
        Expr::Sym(val.to_string())
    }

    pub fn new_proc(func: fn(Expr) -> Expr) -> Expr {
        Expr::Proc(ProcFunc(func))
    }

    pub fn new_cproc(params: Expr, body: Expr,
                     env: Rc<RefCell<env::Env>>) -> Expr {
        Expr::CompProc(vec![params, body], env)
    }

    pub fn is_true(&self) -> bool {
        self.as_bool()
    }

    pub fn is_false(&self) -> bool {
        !self.as_bool()
    }

    pub fn is_pair(&self) -> bool {
        match *self {
            Expr::Pair(_) | Expr::Nil => true,
            _ => false
        }
    }

    pub fn is_empty(&self) -> bool {
        match *self {
            Expr::Nil => true,
            _ => false
        }
    }

    pub fn is_cproc(&self) -> bool {
        match *self {
            Expr::CompProc(_, _) => true,
            _ => false
        }
    }

    pub fn is_last(&self) -> bool {
        assert!(self.is_pair());
        self.cdr().is_empty()
    }

    pub fn is_self(&self) -> bool {
        match *self {
            Expr::Bool(_) | Expr::Int(_) |
            Expr::Char(_) | Expr::Str(_)
                => true ,
            _ => false
        }
    }

    pub fn is_tagged(&self, tag: Expr) -> bool {
        if self.is_pair() {
            let car = self.car();
            return car.is_sym() && car == tag;
        }
        false
    }

    pub fn as_int(&self) -> isize {
        match *self {
            Expr::Int(ref val) => return *val,
            _ => panic!("expect Int")
        }
    }

    pub fn as_bool(&self) -> bool {
        match *self {
            Expr::Bool(ref val) => return *val,
            _ => panic!("expect Bool")
        }
    }

    pub fn as_char(&self) -> char {
        match *self {
            Expr::Char(ref val) => return *val,
            _ => panic!("expect Char")
        }
    }

    pub fn as_proc(&self) -> ProcFunc {
        match *self {
            Expr::Proc(ref val) => val.clone(),
            _ => panic!("expect Proc")
        }
    }

    pub fn as_str(&self) -> String {
        match *self {
            Expr::Str(ref val) => return val.clone(),
            Expr::Sym(ref val) => return val.clone(),
            _ => panic!("expect Str")
        }
    }

    pub fn car(&self) -> Expr {
        match *self {
            Expr::Pair(ref vec) => { return vec[0].clone(); },
            _ => panic!("expect Pair")
        }
    }

    pub fn cdr(&self) -> Expr {
        match *self {
            Expr::Pair(ref vec) => { return vec[1].clone(); },
            _ => panic!("expect Pair")
        }
    }

    pub fn def_var(&self) -> Expr {
        assert!(self.is_def());
        if self.cdr().car().is_sym() {
            self.c("da")
        } else {
            self.c("daa")
        }
    }

    pub fn def_val(&self) -> Expr {
        assert!(self.is_def());
        if self.c("da").is_sym() {
            self.c("dda")
        } else {
            //proc
            self.c("dad").make_lambda(self.c("dd"))
        }
    }

    pub fn make_lambda(&self, body: Expr) -> Expr {
        let lambda = Expr::new_sym("lambda");
        Expr::new_pair(lambda, Expr::new_pair((*self).clone(), body))
    }

    pub fn params(&self) -> Expr {
        match *self {
            Expr::CompProc(ref val, _) => val[0].clone(),
            _ => panic!("expect CompProc")
        }
    }

    pub fn body(&self) -> Expr {
        match *self {
            Expr::CompProc(ref val, _) => val[1].clone(),
            _ => panic!("expect CompProc")
        }
    }

    pub fn c(&self, s: &str) -> Expr {
        assert!(self.is_pair());
        let mut r = self.clone();
        for c in s.to_string().chars() {
            if c == 'a' { r = r.car(); }
            else { r = r.cdr(); }
        }
        r
    }

    fn collect(&self) -> Vec<Expr> {
        let mut res: Vec<Expr> = vec![];
        let mut _exp = self.clone();
        loop {
            let f = _exp.car();
            if f.is_self() {
                res.push(f);
            } else if !f.is_empty() {
                res.push_all(f.collect().as_slice());
            }
            _exp = _exp.cdr();
            if !_exp.is_pair() { break; }
            if _exp.is_empty() { break; }
        }
        res
    }

    pub fn print(&self) {
        match *self {
            Expr::Int(ref ast) =>  println!("{:?}", ast),
            Expr::Str(ref ast) =>  println!("{:?}", ast),
            Expr::Bool(ref ast) => println!("{:?}", ast),
            Expr::Sym(ref ast) => println!("{:?}", ast),
            Expr::Char(ref ast) => println!("{:?}", ast),
            Expr::Pair(_) => {
                print!("(");
                let exps = self.collect();
                for i in 0..exps.len() {
                    exps[i].print();
                    if i != exps.len()-1 {print!(" ");}
                }
                print!(")");
            }
            Expr::Proc(ref ast) => println!("{:?}", ast),
            Expr::CompProc(ref ast, _) => println!("{:?}", ast),
            Expr::Nil => print!("Nil")
        }
    }

}

macro_rules! is_ast_type {
    ($func_name:ident, $type_name:ident) => (impl Expr {
        pub fn $func_name(&self) -> bool {
            match *self {
                Expr::$type_name(_) => true,
                _ => false
            }
        }})
}

is_ast_type!(is_char, Char);
is_ast_type!(is_int, Int);
is_ast_type!(is_sym, Sym);
is_ast_type!(is_str, Str);
is_ast_type!(is_proc, Proc);
is_ast_type!(is_bool, Bool);

macro_rules! is_type {
    ($func_name:ident, $type_str:expr) => (impl Expr {
        pub fn $func_name(&self) -> bool {
            return self.is_tagged(Expr::Sym($type_str.to_string()))
        }
    })
}

is_type!(is_quote, "quote");
is_type!(is_def, "define");
is_type!(is_and, "and");
is_type!(is_or, "or");
is_type!(is_if, "if");
is_type!(is_assign, "set!");
is_type!(is_lambda, "lambda");
is_type!(is_cond, "cond");
is_type!(is_let, "let");
is_type!(is_begin, "begin");

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_ast() {
        let int_node = Expr::Int(3);
        assert!(int_node.as_int() == 3);

        let char_node = Expr::Char('a');
        assert!(char_node.as_char() == 'a');

        let bool_node = Expr::Bool(false);
        assert!(bool_node.as_bool() == false);

        let str_node = Expr::new_str("hello");
        assert!(str_node.as_str() == "hello");
        assert!(str_node.is_self());

        let int_node = Expr::Int(3);
        let str_node = Expr::new_str("hello");
        let pair_node = Expr::new_pair(int_node, str_node);
        let car_node = pair_node.car();
        let cdr_node = pair_node.cdr();
        assert!(car_node.as_int() == 3);
        assert!(cdr_node.as_str() == "hello");
        assert!(!pair_node.is_self());

        let sym_node = Expr::new_sym("sym");
        assert!(sym_node.is_sym());
        assert!(!sym_node.is_self());

        let empty_node = Expr::Nil;
        assert!(empty_node.is_empty());
        assert!(!empty_node.is_self());
    }

    #[test]
    fn test_ast_is_set() {
        macro_rules! test_case {
            ($str_name:expr) => {
                {Expr::new_pair(Expr::new_sym($str_name), Expr::Int(3))}
            }
        }
        assert!(test_case!("let").is_let());
        assert!(test_case!("if").is_if());
        assert!(test_case!("lambda").is_lambda());
        assert!(test_case!("cond").is_cond());
        assert!(test_case!("set!").is_assign());
        assert!(test_case!("begin").is_begin());
        assert!(!test_case!("begin").is_assign());
    }

    #[test]
    fn test_symbol_eq() {
        let aa = Expr::new_sym("else");
        let bb = Expr::new_sym("else");
        assert!(aa == bb);
    }

    #[test]
    fn test_proc() {
        fn _proc(obj: Expr) -> Expr {
            obj.print();
            return Expr::new_sym("ok");
        }

        let proc_node = Expr::new_proc(_proc);
        assert!(proc_node.is_proc());
        assert!(!proc_node.is_cproc());
    }
}

// fn main() {
//     let a = Expr::Int(10is);
//     println!("a: {:?}", a);
//     assert!(a.as_int() == 10is);

//     let b = Expr::Str("hello".to_string());
//     println!("b: {:?}", b);
//     assert!(b.as_str() == "hello".to_string());

//     let b_str = Expr::new_str("hello");
//     assert!(b == b_str);

//     let b_sym = Expr::new_sym("hello");
//     assert!(b_str != b_sym);

//     let c = Expr::Char('a');
//     println!("char: {:?}", c);

//     let c = Expr::new_pair(a, b);
//     println!("c: {:?}", c);

//     let car = c.car();
//     let cdr = c.cdr();
//     println!("car: {:?}", car);
//     println!("cdr: {:?}", cdr);

//     fn _proc(obj: Expr) -> Expr {
//         println!("obj: {:?}", obj);
//         Expr::new_str("OK")
//     }

//     let proc_node = Expr::new_proc(_proc);
//     println!("proc_node: {:?}", proc_node);
//     assert!(proc_node.is_proc());
// }
<commit_msg>clean up<commit_after>use std::cell::RefCell;
use std::rc::Rc;
use std::fmt;
use env;

#[derive(Clone, PartialEq, Show)]
pub enum Expr {
    Int(isize),
    Str(String),
    Sym(String),
    Bool(bool),
    Char(char),
    Pair(Vec<Expr>),
    Proc(ProcFunc),
    CompProc(Vec<Expr>, Rc<RefCell<env::Env>>),
    Nil
}

#[derive(Clone)]
pub struct ProcFunc(fn(Expr) -> Expr);

impl PartialEq for ProcFunc {
    fn eq(&self, o: &ProcFunc) -> bool {
        let _o: *const() = unsafe { ::std::mem::transmute(o)};
        let _s: *const() = unsafe { ::std::mem::transmute(self)};
        _s == _o
    }
    fn ne(&self, o: &ProcFunc) -> bool {
        !self.eq(o)
    }
}

impl ProcFunc {
    pub fn func(&self) -> (fn(Expr) -> Expr) {
        match *self {
            ProcFunc(fun) => fun
        }
    }
}

impl fmt::Show for ProcFunc {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "proc")
    }
}

impl Expr {
    pub fn new_pair(car: Expr, cdr: Expr) -> Expr {
        Expr::Pair(vec![car, cdr])
    }

    pub fn new_str(val: &str) -> Expr {
        Expr::Str(val.to_string())
    }

    pub fn new_sym(val: &str) -> Expr {
        Expr::Sym(val.to_string())
    }

    pub fn new_proc(func: fn(Expr) -> Expr) -> Expr {
        Expr::Proc(ProcFunc(func))
    }

    pub fn new_cproc(params: Expr, body: Expr,
                     env: Rc<RefCell<env::Env>>) -> Expr {
        Expr::CompProc(vec![params, body], env)
    }

    pub fn is_true(&self) -> bool {
        self.as_bool()
    }

    pub fn is_false(&self) -> bool {
        !self.as_bool()
    }

    pub fn is_pair(&self) -> bool {
        match *self {
            Expr::Pair(_) | Expr::Nil => true,
            _ => false
        }
    }

    pub fn is_empty(&self) -> bool {
        match *self {
            Expr::Nil => true,
            _ => false
        }
    }

    pub fn is_cproc(&self) -> bool {
        match *self {
            Expr::CompProc(_, _) => true,
            _ => false
        }
    }

    pub fn is_last(&self) -> bool {
        assert!(self.is_pair());
        self.cdr().is_empty()
    }

    pub fn is_self(&self) -> bool {
        match *self {
            Expr::Bool(_) | Expr::Int(_) |
            Expr::Char(_) | Expr::Str(_)
                => true ,
            _ => false
        }
    }

    pub fn is_tagged(&self, tag: Expr) -> bool {
        if self.is_pair() {
            let car = self.car();
            return car.is_sym() && car == tag;
        }
        false
    }

    pub fn as_int(&self) -> isize {
        match *self {
            Expr::Int(ref val) => return *val,
            _ => panic!("expect Int")
        }
    }

    pub fn as_bool(&self) -> bool {
        match *self {
            Expr::Bool(ref val) => return *val,
            _ => panic!("expect Bool")
        }
    }

    pub fn as_char(&self) -> char {
        match *self {
            Expr::Char(ref val) => return *val,
            _ => panic!("expect Char")
        }
    }

    pub fn as_proc(&self) -> ProcFunc {
        match *self {
            Expr::Proc(ref val) => val.clone(),
            _ => panic!("expect Proc")
        }
    }

    pub fn as_str(&self) -> String {
        match *self {
            Expr::Str(ref val) => return val.clone(),
            Expr::Sym(ref val) => return val.clone(),
            _ => panic!("expect Str")
        }
    }

    pub fn car(&self) -> Expr {
        match *self {
            Expr::Pair(ref vec) => { return vec[0].clone(); },
            _ => panic!("expect Pair")
        }
    }

    pub fn cdr(&self) -> Expr {
        match *self {
            Expr::Pair(ref vec) => { return vec[1].clone(); },
            _ => panic!("expect Pair")
        }
    }

    pub fn def_var(&self) -> Expr {
        assert!(self.is_def());
        if self.cdr().car().is_sym() {
            self.c("da")
        } else {
            self.c("daa")
        }
    }

    pub fn def_val(&self) -> Expr {
        assert!(self.is_def());
        if self.c("da").is_sym() {
            self.c("dda")
        } else {
            //proc
            self.c("dad").make_lambda(self.c("dd"))
        }
    }

    pub fn make_lambda(&self, body: Expr) -> Expr {
        let lambda = Expr::new_sym("lambda");
        Expr::new_pair(lambda, Expr::new_pair((*self).clone(), body))
    }

    pub fn params(&self) -> Expr {
        match *self {
            Expr::CompProc(ref val, _) => val[0].clone(),
            _ => panic!("expect CompProc")
        }
    }

    pub fn body(&self) -> Expr {
        match *self {
            Expr::CompProc(ref val, _) => val[1].clone(),
            _ => panic!("expect CompProc")
        }
    }

    pub fn c(&self, s: &str) -> Expr {
        assert!(self.is_pair());
        let mut r = self.clone();
        for c in s.to_string().chars() {
            if c == 'a' { r = r.car(); }
            else { r = r.cdr(); }
        }
        r
    }

    fn collect(&self) -> Vec<Expr> {
        let mut res: Vec<Expr> = vec![];
        let mut _exp = self.clone();
        loop {
            let f = _exp.car();
            if f.is_self() {
                res.push(f);
            } else if !f.is_empty() {
                res.push_all(f.collect().as_slice());
            }
            _exp = _exp.cdr();
            if !_exp.is_pair() { break; }
            if _exp.is_empty() { break; }
        }
        res
    }

    pub fn print(&self) {
        match *self {
            Expr::Int(ref ast) =>  println!("{:?}", ast),
            Expr::Str(ref ast) =>  println!("{:?}", ast),
            Expr::Bool(ref ast) => println!("{:?}", ast),
            Expr::Sym(ref ast) => println!("{:?}", ast),
            Expr::Char(ref ast) => println!("{:?}", ast),
            Expr::Pair(_) => {
                print!("(");
                let exps = self.collect();
                for i in 0..exps.len() {
                    exps[i].print();
                    if i != exps.len()-1 {print!(" ");}
                }
                print!(")");
            }
            Expr::Proc(ref ast) => println!("{:?}", ast),
            Expr::CompProc(ref ast, _) => println!("{:?}", ast),
            Expr::Nil => print!("Nil")
        }
    }

}

macro_rules! is_ast_type {
    ($func_name:ident, $type_name:ident) => (impl Expr {
        pub fn $func_name(&self) -> bool {
            match *self {
                Expr::$type_name(_) => true,
                _ => false
            }
        }})
}

is_ast_type!(is_char, Char);
is_ast_type!(is_int, Int);
is_ast_type!(is_sym, Sym);
is_ast_type!(is_str, Str);
is_ast_type!(is_proc, Proc);
is_ast_type!(is_bool, Bool);

macro_rules! is_type {
    ($func_name:ident, $type_str:expr) => (impl Expr {
        pub fn $func_name(&self) -> bool {
            return self.is_tagged(Expr::Sym($type_str.to_string()))
        }
    })
}

is_type!(is_quote, "quote");
is_type!(is_def, "define");
is_type!(is_and, "and");
is_type!(is_or, "or");
is_type!(is_if, "if");
is_type!(is_assign, "set!");
is_type!(is_lambda, "lambda");
is_type!(is_cond, "cond");
is_type!(is_let, "let");
is_type!(is_begin, "begin");

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_ast() {
        let int_node = Expr::Int(3);
        assert!(int_node.as_int() == 3);

        let char_node = Expr::Char('a');
        assert!(char_node.as_char() == 'a');

        let bool_node = Expr::Bool(false);
        assert!(bool_node.as_bool() == false);

        let str_node = Expr::new_str("hello");
        assert!(str_node.as_str() == "hello");
        assert!(str_node.is_self());

        let int_node = Expr::Int(3);
        let str_node = Expr::new_str("hello");
        let pair_node = Expr::new_pair(int_node, str_node);
        let car_node = pair_node.car();
        let cdr_node = pair_node.cdr();
        assert!(car_node.as_int() == 3);
        assert!(cdr_node.as_str() == "hello");
        assert!(!pair_node.is_self());

        let sym_node = Expr::new_sym("sym");
        assert!(sym_node.is_sym());
        assert!(!sym_node.is_self());

        let empty_node = Expr::Nil;
        assert!(empty_node.is_empty());
        assert!(!empty_node.is_self());
    }

    #[test]
    fn test_ast_is_set() {
        macro_rules! test_case {
            ($str_name:expr) => {
                {Expr::new_pair(Expr::new_sym($str_name), Expr::Int(3))}
            }
        }
        assert!(test_case!("let").is_let());
        assert!(test_case!("if").is_if());
        assert!(test_case!("lambda").is_lambda());
        assert!(test_case!("cond").is_cond());
        assert!(test_case!("set!").is_assign());
        assert!(test_case!("begin").is_begin());
        assert!(!test_case!("begin").is_assign());
    }

    #[test]
    fn test_symbol_eq() {
        let aa = Expr::new_sym("else");
        let bb = Expr::new_sym("else");
        assert!(aa == bb);
    }

    #[test]
    fn test_proc() {
        fn _proc(obj: Expr) -> Expr {
            obj.print();
            return Expr::new_sym("ok");
        }

        let proc_node = Expr::new_proc(_proc);
        assert!(proc_node.is_proc());
        assert!(!proc_node.is_cproc());
    }
}
<|endoftext|>


<commit_before>import core::option;
import ctypes::*;

export libc;
export libc_constants;
export pipe;
export fd_FILE;
export close;
export fclose;
export waitpid;
export getcwd;
export exec_suffix;
export target_os;
export dylib_filename;
export get_exe_path;
export fsync_fd;

// FIXME Refactor into unix_os module or some such. Doesn't
// seem to work right now.

#[nolink]
#[abi = "cdecl"]
native mod libc {
    fn read(fd: fd_t, buf: *u8, count: size_t) -> ssize_t;
    fn write(fd: fd_t, buf: *u8, count: size_t) -> ssize_t;
    fn fread(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t;
    fn fwrite(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t;
    fn open(s: str::sbuf, flags: c_int, mode: unsigned) -> fd_t;
    fn close(fd: fd_t) -> c_int;
    type FILE;
    fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE;
    fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE;
    fn fflush(f: FILE) -> c_int;
    fn fsync(fd: fd_t) -> c_int;
    fn fileno(f: FILE) -> fd_t;
    fn fclose(f: FILE);
    fn fgetc(f: FILE) -> c_int;
    fn ungetc(c: c_int, f: FILE);
    fn feof(f: FILE) -> c_int;
    fn fseek(f: FILE, offset: long, whence: c_int) -> c_int;
    fn ftell(f: FILE) -> long;
    type dir;
    fn opendir(d: str::sbuf) -> dir;
    fn closedir(d: dir) -> c_int;
    type dirent;
    fn readdir(d: dir) -> dirent;
    fn getenv(n: str::sbuf) -> str::sbuf;
    fn setenv(n: str::sbuf, v: str::sbuf, overwrite: c_int) -> c_int;
    fn unsetenv(n: str::sbuf) -> c_int;
    fn pipe(buf: *mutable c_int) -> c_int;
    fn waitpid(pid: pid_t, &status: c_int, options: c_int) -> c_int;
    fn mkdir(s: str::sbuf, mode: c_int) -> c_int;
    fn rmdir(s: str::sbuf) -> c_int;
    fn chdir(s: str::sbuf) -> c_int;

    // FIXME: Needs varags
    fn fcntl(fd: fd_t, cmd: c_int) -> c_int;
}

mod libc_constants {
    const O_RDONLY: c_int    = 0i32;
    const O_WRONLY: c_int    = 1i32;
    const O_RDWR: c_int      = 2i32;
    const O_APPEND: c_int    = 8i32;
    const O_CREAT: c_int     = 512i32;
    const O_EXCL: c_int      = 2048i32;
    const O_TRUNC: c_int     = 1024i32;
    const O_TEXT: c_int      = 0i32;    // nonexistent in darwin libc
    const O_BINARY: c_int    = 0i32;    // nonexistent in darwin libc

    const S_IRUSR: unsigned  = 256u32;
    const S_IWUSR: unsigned  = 128u32;

    const F_FULLFSYNC: c_int = 51i32;
}

fn pipe() -> {in: fd_t, out: fd_t} {
    let fds = {mutable in: 0i32, mutable out: 0i32};
    assert (os::libc::pipe(ptr::mut_addr_of(fds.in)) == 0i32);
    ret {in: fds.in, out: fds.out};
}

fn fd_FILE(fd: fd_t) -> libc::FILE {
    ret str::as_buf("r", {|modebuf| libc::fdopen(fd, modebuf) });
}

fn close(fd: fd_t) -> c_int {
    libc::close(fd)
}

fn fclose(file: libc::FILE) {
    libc::fclose(file)
}

fn waitpid(pid: pid_t) -> i32 {
    let status = 0i32;
    assert (os::libc::waitpid(pid, status, 0i32) != -1i32);
    ret status;
}

fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int {
    alt level {
      io::fsync::fsync. { ret libc::fsync(fd); }
      _ {
        // According to man fnctl, the ok retval is only specified to be !=-1
        if (libc::fcntl(libc_constants::F_FULLFSYNC, fd) == -1 as c_int)
            { ret -1 as c_int; }
        else
            { ret 0 as c_int; }
      }
    }
}

#[abi = "cdecl"]
native mod rustrt {
    fn rust_getcwd() -> str;
}

fn getcwd() -> str { ret rustrt::rust_getcwd(); }

#[link_name = ""]               // FIXME remove after #[nolink] is snapshotted
#[nolink]
#[abi = "cdecl"]
native mod mac_libc {
    fn _NSGetExecutablePath(buf: str::sbuf,
                            bufsize: *mutable uint32_t) -> c_int;
}

fn exec_suffix() -> str { ret ""; }

fn target_os() -> str { ret "macos"; }

fn dylib_filename(base: str) -> str { ret "lib" + base + ".dylib"; }

fn get_exe_path() -> option::t<fs::path> {
    // FIXME: This doesn't handle the case where the buffer is too small
    let bufsize = 1023u32;
    let path = str::unsafe_from_bytes(vec::init_elt(0u8, bufsize as uint));
    ret str::as_buf(path, { |path_buf|
        if mac_libc::_NSGetExecutablePath(path_buf,
                                          ptr::mut_addr_of(bufsize)) == 0i32 {
            option::some(fs::dirname(path) + fs::path_sep())
        } else {
            option::none
        }
    });
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>libstd: Temporarily use #[link_name = ""]<commit_after>import core::option;
import ctypes::*;

export libc;
export libc_constants;
export pipe;
export fd_FILE;
export close;
export fclose;
export waitpid;
export getcwd;
export exec_suffix;
export target_os;
export dylib_filename;
export get_exe_path;
export fsync_fd;

// FIXME Refactor into unix_os module or some such. Doesn't
// seem to work right now.

#[link_name = ""] // FIXME: Remove after snapshotting
#[nolink]
#[abi = "cdecl"]
native mod libc {
    fn read(fd: fd_t, buf: *u8, count: size_t) -> ssize_t;
    fn write(fd: fd_t, buf: *u8, count: size_t) -> ssize_t;
    fn fread(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t;
    fn fwrite(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t;
    fn open(s: str::sbuf, flags: c_int, mode: unsigned) -> fd_t;
    fn close(fd: fd_t) -> c_int;
    type FILE;
    fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE;
    fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE;
    fn fflush(f: FILE) -> c_int;
    fn fsync(fd: fd_t) -> c_int;
    fn fileno(f: FILE) -> fd_t;
    fn fclose(f: FILE);
    fn fgetc(f: FILE) -> c_int;
    fn ungetc(c: c_int, f: FILE);
    fn feof(f: FILE) -> c_int;
    fn fseek(f: FILE, offset: long, whence: c_int) -> c_int;
    fn ftell(f: FILE) -> long;
    type dir;
    fn opendir(d: str::sbuf) -> dir;
    fn closedir(d: dir) -> c_int;
    type dirent;
    fn readdir(d: dir) -> dirent;
    fn getenv(n: str::sbuf) -> str::sbuf;
    fn setenv(n: str::sbuf, v: str::sbuf, overwrite: c_int) -> c_int;
    fn unsetenv(n: str::sbuf) -> c_int;
    fn pipe(buf: *mutable c_int) -> c_int;
    fn waitpid(pid: pid_t, &status: c_int, options: c_int) -> c_int;
    fn mkdir(s: str::sbuf, mode: c_int) -> c_int;
    fn rmdir(s: str::sbuf) -> c_int;
    fn chdir(s: str::sbuf) -> c_int;

    // FIXME: Needs varags
    fn fcntl(fd: fd_t, cmd: c_int) -> c_int;
}

mod libc_constants {
    const O_RDONLY: c_int    = 0i32;
    const O_WRONLY: c_int    = 1i32;
    const O_RDWR: c_int      = 2i32;
    const O_APPEND: c_int    = 8i32;
    const O_CREAT: c_int     = 512i32;
    const O_EXCL: c_int      = 2048i32;
    const O_TRUNC: c_int     = 1024i32;
    const O_TEXT: c_int      = 0i32;    // nonexistent in darwin libc
    const O_BINARY: c_int    = 0i32;    // nonexistent in darwin libc

    const S_IRUSR: unsigned  = 256u32;
    const S_IWUSR: unsigned  = 128u32;

    const F_FULLFSYNC: c_int = 51i32;
}

fn pipe() -> {in: fd_t, out: fd_t} {
    let fds = {mutable in: 0i32, mutable out: 0i32};
    assert (os::libc::pipe(ptr::mut_addr_of(fds.in)) == 0i32);
    ret {in: fds.in, out: fds.out};
}

fn fd_FILE(fd: fd_t) -> libc::FILE {
    ret str::as_buf("r", {|modebuf| libc::fdopen(fd, modebuf) });
}

fn close(fd: fd_t) -> c_int {
    libc::close(fd)
}

fn fclose(file: libc::FILE) {
    libc::fclose(file)
}

fn waitpid(pid: pid_t) -> i32 {
    let status = 0i32;
    assert (os::libc::waitpid(pid, status, 0i32) != -1i32);
    ret status;
}

fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int {
    alt level {
      io::fsync::fsync. { ret libc::fsync(fd); }
      _ {
        // According to man fnctl, the ok retval is only specified to be !=-1
        if (libc::fcntl(libc_constants::F_FULLFSYNC, fd) == -1 as c_int)
            { ret -1 as c_int; }
        else
            { ret 0 as c_int; }
      }
    }
}

#[abi = "cdecl"]
native mod rustrt {
    fn rust_getcwd() -> str;
}

fn getcwd() -> str { ret rustrt::rust_getcwd(); }

#[link_name = ""]               // FIXME remove after #[nolink] is snapshotted
#[nolink]
#[abi = "cdecl"]
native mod mac_libc {
    fn _NSGetExecutablePath(buf: str::sbuf,
                            bufsize: *mutable uint32_t) -> c_int;
}

fn exec_suffix() -> str { ret ""; }

fn target_os() -> str { ret "macos"; }

fn dylib_filename(base: str) -> str { ret "lib" + base + ".dylib"; }

fn get_exe_path() -> option::t<fs::path> {
    // FIXME: This doesn't handle the case where the buffer is too small
    let bufsize = 1023u32;
    let path = str::unsafe_from_bytes(vec::init_elt(0u8, bufsize as uint));
    ret str::as_buf(path, { |path_buf|
        if mac_libc::_NSGetExecutablePath(path_buf,
                                          ptr::mut_addr_of(bufsize)) == 0i32 {
            option::some(fs::dirname(path) + fs::path_sep())
        } else {
            option::none
        }
    });
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::collections::{HashMap, BTreeMap};
use std::{fmt, cmp};

use base32;
use url::Url;
use ring::digest;

use disk;
use bencode::BEncode;
use util::{hash_to_id, id_to_hash};

#[derive(Serialize, Deserialize, Clone)]
pub struct Info {
    pub name: String,
    pub announce: String,
    pub piece_len: u32,
    pub total_len: u64,
    pub hashes: Vec<Vec<u8>>,
    pub hash: [u8; 20],
    pub files: Vec<File>,
    pub file_idx: HashMap<PathBuf, usize>,
    pub private: bool,
    pub be_name: Option<Vec<u8>>,
}

impl fmt::Debug for Info {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Torrent Info {{
                name: {:?},
                announce: {:?},
                piece_len: {:?},
                total_len: {:?},
                hash: {:?},
                files: {:?}
            }}",
            self.name,
            self.announce,
            self.piece_len,
            self.total_len,
            hash_to_id(&self.hash),
            self.files
        )
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct File {
    pub path: PathBuf,
    pub length: u64,
}

impl File {
    fn from_bencode(data: BEncode) -> Result<File, &'static str> {
        let mut d = data.into_dict().ok_or("File must be a dictionary type!")?;
        match (d.remove("name"), d.remove("path"), d.remove("length")) {
            (Some(v), None, Some(l)) => {
                let f = File {
                    path: PathBuf::from(v.into_string().ok_or("Path must be a valid string.")?),
                    length: l.into_int().ok_or("File length must be a valid int")? as u64,
                };
                Ok(f)
            }
            (None, Some(path), Some(l)) => {
                let mut p = PathBuf::new();
                for dir in path.into_list().ok_or("File path should be a list")? {
                    p.push(
                        dir.into_string().ok_or("File path parts should be strings")?,
                    );
                }
                let f = File {
                    path: p,
                    length: l.into_int().ok_or("File length must be a valid int")? as u64,
                };
                Ok(f)
            }
            _ => Err("File dict must contain length and name or path"),
        }
    }
}

impl Info {
    pub fn from_magnet(data: &str) -> Result<Info, &'static str> {
        let url = match Url::parse(data) {
            Ok(u) => u,
            Err(_) => return Err("Failed to parse magnet URL!"),
        };

        if url.scheme() != "magnet" {
            return Err("magnet URL must use magnet URL scheme");
        };
        let hash = url.query_pairs()
            .find(|&(ref k, ref v)| k == "xt" && v.starts_with("urn:btih:"))
            .and_then(|(_, ref v)| {
                id_to_hash(&v[9..]).or_else(|| {
                    base32::decode(base32::Alphabet::RFC4648 { padding: true }, &v[9..])
                        .and_then(|b| {
                            if b.len() != 20 {
                                return None;
                            }
                            let mut a = [0; 20];
                            (&mut a[..]).copy_from_slice(&b);
                            Some(a)
                        })
                })
            })
            .ok_or("No hash found in magnet")?;
        let announce = url.query_pairs()
            .find(|&(ref k, _)| k == "tr")
            .map(|(_, ref v)| v.to_string())
            .unwrap_or_else(|| "".to_owned());
        let name = url.query_pairs()
            .find(|&(ref k, _)| k == "dn")
            .map(|(_, ref v)| v.to_string())
            .unwrap_or_else(|| "".to_owned());
        Ok(Info {
            name,
            announce,
            piece_len: 0,
            total_len: 0,
            hashes: vec![],
            hash,
            files: vec![],
            file_idx: HashMap::new(),
            private: false,
            be_name: None,
        })
    }

    pub fn complete(&self) -> bool {
        !self.hashes.is_empty()
    }

    pub fn to_bencode(&self) -> BEncode {
        let mut info = BTreeMap::new();
        if let Some(ref n) = self.be_name {
            info.insert("name".to_owned(), BEncode::String(n.clone()));
        }
        if self.private {
            info.insert("private".to_owned(), BEncode::Int(1));
        }
        info.insert(
            "piece length".to_owned(),
            BEncode::Int(i64::from(self.piece_len)),
        );
        let mut pieces = Vec::with_capacity(self.hashes.len() * 20);
        for h in &self.hashes {
            pieces.extend_from_slice(h);
        }
        info.insert("pieces".to_owned(), BEncode::String(pieces));
        if self.files.len() == 1 {
            info.insert(
                "length".to_owned(),
                BEncode::Int(self.files[0].length as i64),
            );
        } else {
            let files = self.files
                .iter()
                .map(|f| {
                    let mut fb = BTreeMap::new();
                    fb.insert("length".to_owned(), BEncode::Int(f.length as i64));
                    fb.insert(
                        "path".to_owned(),
                        BEncode::String(
                            f.path
                                .clone()
                                .into_os_string()
                                .into_string()
                                .unwrap()
                                .into_bytes(),
                        ),
                    );
                    BEncode::Dict(fb)
                })
                .collect();
            info.insert("files".to_owned(), BEncode::List(files));
        }
        BEncode::Dict(info)
    }

    pub fn from_bencode(data: BEncode) -> Result<Info, &'static str> {
        data.into_dict()
            .and_then(|mut d| {
                d.remove("info").and_then(|i| i.into_dict()).map(|i| (d, i))
            })
            .ok_or("invalid info field")
            .and_then(|(mut d, mut i)| {
                let mut info_bytes = Vec::new();
                BEncode::Dict(i.clone()).encode(&mut info_bytes).unwrap();
                let mut ctx = digest::Context::new(&digest::SHA1);
                ctx.update(&info_bytes[..]);
                let digest = ctx.finish();
                let mut hash = [0u8; 20];
                hash.copy_from_slice(digest.as_ref());

                let a = d.remove("announce").and_then(|a| a.into_string()).ok_or(
                    "Info must have announce url",
                )?;
                let pl = i.remove("piece length").and_then(|i| i.into_int()).ok_or(
                    "Info must specify piece length",
                )?;
                let hashes = i.remove("pieces")
                    .and_then(|p| p.into_bytes())
                    .map(|mut p| {
                        let mut v = Vec::new();
                        while !p.is_empty() {
                            let remaining = p.split_off(20);
                            v.push(p);
                            p = remaining;
                        }
                        v
                    })
                    .ok_or("Info must provide valid hashes")?;

                let private = if let Some(v) = i.remove("private") {
                    v.into_int()
                        .and_then(|p| if p == 1 { Some(true) } else { None })
                        .ok_or("private key must be an integer equal to 1 if present!")?
                } else {
                    false
                };

                let be_name = if let Some(v) = i.get("name").cloned() {
                    Some(v.into_bytes().ok_or("name field must be a bitstring!")?)
                } else {
                    None
                };

                let files = parse_bencode_files(i)?;
                let name = if files.is_empty() {
                    files[0]
                        .path
                        .clone()
                        .into_os_string()
                        .into_string()
                        .map_err(|_| "Only UTF8 paths are accepted")?
                } else if !files[0].path.has_root() {
                    let mut piter = files[0].path.components();
                    piter
                        .next()
                        .unwrap()
                        .as_os_str()
                        .to_os_string()
                        .into_string()
                        .map_err(|_| "Only UTF8 paths are accepted")?
                } else {
                    unreachable!()
                };

                let mut file_idx = HashMap::new();
                for (i, file) in files.iter().enumerate() {
                    file_idx.insert(file.path.clone(), i);
                }
                let total_len = files.iter().map(|f| f.length).sum();
                Ok(Info {
                    name,
                    announce: a,
                    piece_len: pl as u32,
                    hashes,
                    hash,
                    files,
                    file_idx,
                    total_len,
                    private,
                    be_name,
                })
            })
    }

    #[cfg(test)]
    pub fn with_pieces(pieces: usize) -> Info {
        Info {
            name: String::from(""),
            announce: String::from(""),
            piece_len: 16_384,
            total_len: 16_384 * pieces as u64,
            hashes: vec![vec![0u8]; pieces],
            hash: [0u8; 20],
            files: vec![],
            file_idx: HashMap::new(),
            private: false,
            be_name: None,
        }
    }

    pub fn block_len(&self, idx: u32, offset: u32) -> u32 {
        if idx != self.pieces() - 1 {
            16_384
        } else {
            let last_piece_len =
                (self.total_len - u64::from(self.piece_len) * u64::from(self.pieces()) - 1) as u32;
            // Note this is not the real last block len, just what it will be IF the offset really
            // is for the last block
            let last_block_len = last_piece_len - offset;
            if offset < last_piece_len && last_block_len <= 16_384 {
                last_block_len
            } else {
                16_384
            }
        }
    }

    pub fn piece_len(&self, idx: u32) -> u32 {
        if !self.complete() {
            return 0;
        }
        if idx != self.pieces().saturating_sub(1) {
            self.piece_len
        } else {
            (self.total_len - u64::from(self.piece_len) * (u64::from(self.pieces()) - 1)) as u32
        }
    }

    pub fn pieces(&self) -> u32 {
        self.hashes.len() as u32
    }

    /// Calculates the file offsets for a given block at index/begin
    pub fn block_disk_locs(&self, index: u32, begin: u32) -> Vec<disk::Location> {
        let len = self.block_len(index, begin);
        self.calc_disk_locs(index, begin, len)
    }

    /// Calculates the file offsets for a given piece at index
    pub fn piece_disk_locs(&self, index: u32) -> Vec<disk::Location> {
        let len = self.piece_len(index);
        self.calc_disk_locs(index, 0, len)
    }

    /// Calculates the file offsets for a given index, begin, and block length.
    fn calc_disk_locs(&self, index: u32, begin: u32, len: u32) -> Vec<disk::Location> {
        let mut len = u64::from(len);
        // The absolute byte offset where we start processing data.
        let mut cur_start = u64::from(index) * u64::from(self.piece_len) + u64::from(begin);
        // Current index of the data block we're writing
        let mut data_start = 0;
        // The current file end length.
        let mut fidx = 0;
        // Iterate over all file lengths, if we find any which end a bigger
        // idx than cur_start, write from cur_start..cur_start + file_write_len for that file
        // and continue if we're now at the end of the file.
        let mut locs = Vec::new();
        for f in &self.files {
            fidx += f.length;
            if cur_start < fidx {
                let file_write_len = cmp::min(fidx - cur_start, len);
                let offset = cur_start - (fidx - f.length);
                if file_write_len == len {
                    // The file is longer than our len, just write to it,
                    // exit loop
                    locs.push(disk::Location::new(
                        f.path.clone(),
                        offset,
                        data_start,
                        data_start + file_write_len,
                    ));
                    break;
                } else {
                    // Write to the end of file, continue
                    locs.push(disk::Location::new(
                        f.path.clone(),
                        offset,
                        data_start,
                        data_start + file_write_len,
                    ));
                    len -= file_write_len;
                    cur_start += file_write_len;
                    data_start += file_write_len;
                }
            }
        }
        locs
    }
}

fn parse_bencode_files(mut data: BTreeMap<String, BEncode>) -> Result<Vec<File>, &'static str> {
    match data.remove("files").and_then(|l| l.into_list()) {
        Some(fs) => {
            let mut path = PathBuf::new();
            path.push(data.remove("name").and_then(|v| v.into_string()).ok_or(
                "Multifile mode must have a name field",
            )?);
            let mut files = Vec::new();
            for f in fs {
                let mut file = File::from_bencode(f)?;
                file.path = path.join(file.path);
                files.push(file);
            }
            Ok(files)
        }
        None => File::from_bencode(BEncode::Dict(data)).map(|f| vec![f]),
    }
}
<commit_msg>Ensure that piece len is properly calculated for block sizes<commit_after>use std::path::PathBuf;
use std::collections::{HashMap, BTreeMap};
use std::{fmt, cmp};

use base32;
use url::Url;
use ring::digest;

use disk;
use bencode::BEncode;
use util::{hash_to_id, id_to_hash};

#[derive(Serialize, Deserialize, Clone)]
pub struct Info {
    pub name: String,
    pub announce: String,
    pub piece_len: u32,
    pub total_len: u64,
    pub hashes: Vec<Vec<u8>>,
    pub hash: [u8; 20],
    pub files: Vec<File>,
    pub file_idx: HashMap<PathBuf, usize>,
    pub private: bool,
    pub be_name: Option<Vec<u8>>,
}

impl fmt::Debug for Info {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Torrent Info {{
                name: {:?},
                announce: {:?},
                piece_len: {:?},
                total_len: {:?},
                hash: {:?},
                files: {:?}
            }}",
            self.name,
            self.announce,
            self.piece_len,
            self.total_len,
            hash_to_id(&self.hash),
            self.files
        )
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct File {
    pub path: PathBuf,
    pub length: u64,
}

impl File {
    fn from_bencode(data: BEncode) -> Result<File, &'static str> {
        let mut d = data.into_dict().ok_or("File must be a dictionary type!")?;
        match (d.remove("name"), d.remove("path"), d.remove("length")) {
            (Some(v), None, Some(l)) => {
                let f = File {
                    path: PathBuf::from(v.into_string().ok_or("Path must be a valid string.")?),
                    length: l.into_int().ok_or("File length must be a valid int")? as u64,
                };
                Ok(f)
            }
            (None, Some(path), Some(l)) => {
                let mut p = PathBuf::new();
                for dir in path.into_list().ok_or("File path should be a list")? {
                    p.push(
                        dir.into_string().ok_or("File path parts should be strings")?,
                    );
                }
                let f = File {
                    path: p,
                    length: l.into_int().ok_or("File length must be a valid int")? as u64,
                };
                Ok(f)
            }
            _ => Err("File dict must contain length and name or path"),
        }
    }
}

impl Info {
    pub fn from_magnet(data: &str) -> Result<Info, &'static str> {
        let url = match Url::parse(data) {
            Ok(u) => u,
            Err(_) => return Err("Failed to parse magnet URL!"),
        };

        if url.scheme() != "magnet" {
            return Err("magnet URL must use magnet URL scheme");
        };
        let hash = url.query_pairs()
            .find(|&(ref k, ref v)| k == "xt" && v.starts_with("urn:btih:"))
            .and_then(|(_, ref v)| {
                id_to_hash(&v[9..]).or_else(|| {
                    base32::decode(base32::Alphabet::RFC4648 { padding: true }, &v[9..])
                        .and_then(|b| {
                            if b.len() != 20 {
                                return None;
                            }
                            let mut a = [0; 20];
                            (&mut a[..]).copy_from_slice(&b);
                            Some(a)
                        })
                })
            })
            .ok_or("No hash found in magnet")?;
        let announce = url.query_pairs()
            .find(|&(ref k, _)| k == "tr")
            .map(|(_, ref v)| v.to_string())
            .unwrap_or_else(|| "".to_owned());
        let name = url.query_pairs()
            .find(|&(ref k, _)| k == "dn")
            .map(|(_, ref v)| v.to_string())
            .unwrap_or_else(|| "".to_owned());
        Ok(Info {
            name,
            announce,
            piece_len: 0,
            total_len: 0,
            hashes: vec![],
            hash,
            files: vec![],
            file_idx: HashMap::new(),
            private: false,
            be_name: None,
        })
    }

    pub fn complete(&self) -> bool {
        !self.hashes.is_empty()
    }

    pub fn to_bencode(&self) -> BEncode {
        let mut info = BTreeMap::new();
        if let Some(ref n) = self.be_name {
            info.insert("name".to_owned(), BEncode::String(n.clone()));
        }
        if self.private {
            info.insert("private".to_owned(), BEncode::Int(1));
        }
        info.insert(
            "piece length".to_owned(),
            BEncode::Int(i64::from(self.piece_len)),
        );
        let mut pieces = Vec::with_capacity(self.hashes.len() * 20);
        for h in &self.hashes {
            pieces.extend_from_slice(h);
        }
        info.insert("pieces".to_owned(), BEncode::String(pieces));
        if self.files.len() == 1 {
            info.insert(
                "length".to_owned(),
                BEncode::Int(self.files[0].length as i64),
            );
        } else {
            let files = self.files
                .iter()
                .map(|f| {
                    let mut fb = BTreeMap::new();
                    fb.insert("length".to_owned(), BEncode::Int(f.length as i64));
                    fb.insert(
                        "path".to_owned(),
                        BEncode::String(
                            f.path
                                .clone()
                                .into_os_string()
                                .into_string()
                                .unwrap()
                                .into_bytes(),
                        ),
                    );
                    BEncode::Dict(fb)
                })
                .collect();
            info.insert("files".to_owned(), BEncode::List(files));
        }
        BEncode::Dict(info)
    }

    pub fn from_bencode(data: BEncode) -> Result<Info, &'static str> {
        data.into_dict()
            .and_then(|mut d| {
                d.remove("info").and_then(|i| i.into_dict()).map(|i| (d, i))
            })
            .ok_or("invalid info field")
            .and_then(|(mut d, mut i)| {
                let mut info_bytes = Vec::new();
                BEncode::Dict(i.clone()).encode(&mut info_bytes).unwrap();
                let mut ctx = digest::Context::new(&digest::SHA1);
                ctx.update(&info_bytes[..]);
                let digest = ctx.finish();
                let mut hash = [0u8; 20];
                hash.copy_from_slice(digest.as_ref());

                let a = d.remove("announce").and_then(|a| a.into_string()).ok_or(
                    "Info must have announce url",
                )?;
                let pl = i.remove("piece length").and_then(|i| i.into_int()).ok_or(
                    "Info must specify piece length",
                )?;
                let hashes = i.remove("pieces")
                    .and_then(|p| p.into_bytes())
                    .map(|mut p| {
                        let mut v = Vec::new();
                        while !p.is_empty() {
                            let remaining = p.split_off(20);
                            v.push(p);
                            p = remaining;
                        }
                        v
                    })
                    .ok_or("Info must provide valid hashes")?;

                let private = if let Some(v) = i.remove("private") {
                    v.into_int()
                        .and_then(|p| if p == 1 { Some(true) } else { None })
                        .ok_or("private key must be an integer equal to 1 if present!")?
                } else {
                    false
                };

                let be_name = if let Some(v) = i.get("name").cloned() {
                    Some(v.into_bytes().ok_or("name field must be a bitstring!")?)
                } else {
                    None
                };

                let files = parse_bencode_files(i)?;
                let name = if files.is_empty() {
                    files[0]
                        .path
                        .clone()
                        .into_os_string()
                        .into_string()
                        .map_err(|_| "Only UTF8 paths are accepted")?
                } else if !files[0].path.has_root() {
                    let mut piter = files[0].path.components();
                    piter
                        .next()
                        .unwrap()
                        .as_os_str()
                        .to_os_string()
                        .into_string()
                        .map_err(|_| "Only UTF8 paths are accepted")?
                } else {
                    unreachable!()
                };

                let mut file_idx = HashMap::new();
                for (i, file) in files.iter().enumerate() {
                    file_idx.insert(file.path.clone(), i);
                }
                let total_len = files.iter().map(|f| f.length).sum();
                Ok(Info {
                    name,
                    announce: a,
                    piece_len: pl as u32,
                    hashes,
                    hash,
                    files,
                    file_idx,
                    total_len,
                    private,
                    be_name,
                })
            })
    }

    #[cfg(test)]
    pub fn with_pieces(pieces: usize) -> Info {
        Info {
            name: String::from(""),
            announce: String::from(""),
            piece_len: 16_384,
            total_len: 16_384 * pieces as u64,
            hashes: vec![vec![0u8]; pieces],
            hash: [0u8; 20],
            files: vec![],
            file_idx: HashMap::new(),
            private: false,
            be_name: None,
        }
    }

    pub fn block_len(&self, idx: u32, offset: u32) -> u32 {
        if idx != self.pieces() - 1 {
            16_384
        } else {
            let last_piece_len = self.piece_len(idx);
            // Note this is not the real last block len, just what it will be IF the offset really
            // is for the last block
            let last_block_len = last_piece_len - offset;
            if offset < last_piece_len && last_block_len <= 16_384 {
                last_block_len
            } else {
                16_384
            }
        }
    }

    pub fn piece_len(&self, idx: u32) -> u32 {
        if !self.complete() {
            return 0;
        }
        if idx != self.pieces().saturating_sub(1) {
            self.piece_len
        } else {
            (self.total_len - u64::from(self.piece_len) * (u64::from(self.pieces()) - 1)) as u32
        }
    }

    pub fn pieces(&self) -> u32 {
        self.hashes.len() as u32
    }

    /// Calculates the file offsets for a given block at index/begin
    pub fn block_disk_locs(&self, index: u32, begin: u32) -> Vec<disk::Location> {
        let len = self.block_len(index, begin);
        self.calc_disk_locs(index, begin, len)
    }

    /// Calculates the file offsets for a given piece at index
    pub fn piece_disk_locs(&self, index: u32) -> Vec<disk::Location> {
        let len = self.piece_len(index);
        self.calc_disk_locs(index, 0, len)
    }

    /// Calculates the file offsets for a given index, begin, and block length.
    fn calc_disk_locs(&self, index: u32, begin: u32, len: u32) -> Vec<disk::Location> {
        let mut len = u64::from(len);
        // The absolute byte offset where we start processing data.
        let mut cur_start = u64::from(index) * u64::from(self.piece_len) + u64::from(begin);
        // Current index of the data block we're writing
        let mut data_start = 0;
        // The current file end length.
        let mut fidx = 0;
        // Iterate over all file lengths, if we find any which end a bigger
        // idx than cur_start, write from cur_start..cur_start + file_write_len for that file
        // and continue if we're now at the end of the file.
        let mut locs = Vec::new();
        for f in &self.files {
            fidx += f.length;
            if cur_start < fidx {
                let file_write_len = cmp::min(fidx - cur_start, len);
                let offset = cur_start - (fidx - f.length);
                if file_write_len == len {
                    // The file is longer than our len, just write to it,
                    // exit loop
                    locs.push(disk::Location::new(
                        f.path.clone(),
                        offset,
                        data_start,
                        data_start + file_write_len,
                    ));
                    break;
                } else {
                    // Write to the end of file, continue
                    locs.push(disk::Location::new(
                        f.path.clone(),
                        offset,
                        data_start,
                        data_start + file_write_len,
                    ));
                    len -= file_write_len;
                    cur_start += file_write_len;
                    data_start += file_write_len;
                }
            }
        }
        locs
    }
}

fn parse_bencode_files(mut data: BTreeMap<String, BEncode>) -> Result<Vec<File>, &'static str> {
    match data.remove("files").and_then(|l| l.into_list()) {
        Some(fs) => {
            let mut path = PathBuf::new();
            path.push(data.remove("name").and_then(|v| v.into_string()).ok_or(
                "Multifile mode must have a name field",
            )?);
            let mut files = Vec::new();
            for f in fs {
                let mut file = File::from_bencode(f)?;
                file.path = path.join(file.path);
                files.push(file);
            }
            Ok(files)
        }
        None => File::from_bencode(BEncode::Dict(data)).map(|f| vec![f]),
    }
}
<|endoftext|>