<commit_before>use baseline::codegen::CondCode;
use cpu::asm;
use cpu::asm::*;
use cpu::reg::*;
use cpu::{Mem, Reg};
use lexer::position::Position;
use masm::{MacroAssembler, Label};
use mem::ptr_width;
use object::IntArray;
use os::signal::Trap;
use ty::MachineMode;
use vtable::VTable;

impl MacroAssembler {
    pub fn prolog(&mut self, stacksize: i32) {
        self.emit_u32(asm::stp_pre(1, REG_FP, REG_LR, REG_SP, -2));
        self.emit_u32(asm::add_reg(1, REG_FP, REG_SP, REG_ZERO));

        if stacksize > 0 {
            let scratch = get_scratch();
            self.load_int_const(MachineMode::Ptr, scratch, stacksize);
            self.emit_u32(asm::sub_reg(1, REG_SP, REG_SP, scratch));
        }
    }

    pub fn epilog(&mut self, stacksize: i32) {
        if stacksize > 0 {
            let scratch = get_scratch();
            self.load_int_const(MachineMode::Ptr, scratch, stacksize);
            self.emit_u32(asm::add_reg(1, REG_SP, REG_SP, scratch));
        }

        self.emit_u32(asm::add_reg(1, REG_SP, REG_FP, REG_ZERO));
        self.emit_u32(asm::ldp_post(1, REG_FP, REG_LR, REG_SP, 2));
        self.emit_u32(asm::ret());
    }

    pub fn direct_call(&mut self, ptr: *const u8) {
        let disp = self.add_addr(ptr);
        let pos = self.pos() as i32;

        let scratch = get_scratch();

        self.load_constpool(REG_RESULT, disp + pos);
        self.emit_u32(asm::br(scratch));
    }

    pub fn indirect_call(&mut self, index: u32) {
        let obj = REG_PARAMS[0];

        // REG_RESULT = [obj] (load vtable)
        self.load_mem(MachineMode::Ptr, REG_RESULT, Mem::Base(obj, 0));

        // calculate offset of VTable entry
        let disp = VTable::offset_of_method_table() + (index as i32) * ptr_width();

        // load vtable entry
        self.load_mem(MachineMode::Ptr, REG_RESULT, Mem::Base(REG_RESULT, disp));

        // call *REG_RESULT
        self.emit_u32(asm::blr(REG_RESULT));
    }

    pub fn load_array_elem(&mut self, mode: MachineMode, dest: Reg, array: Reg, index: Reg) {
        assert!(mode == MachineMode::Int32);

        self.load_mem(mode,
                      dest,
                      Mem::Index(array, index, mode.size(), IntArray::offset_of_data()));
    }

    pub fn store_array_elem(&mut self, mode: MachineMode, array: Reg, index: Reg, value: Reg) {
        assert!(mode == MachineMode::Int32);

        self.store_mem(MachineMode::Int32,
                       Mem::Index(array, index, 4, IntArray::offset_of_data()),
                       value);
    }

    pub fn test_if_nil_bailout(&mut self, pos: Position, reg: Reg) {
        self.cmp_reg(MachineMode::Ptr, reg, reg);

        let lbl = self.create_label();
        self.jump_if(CondCode::Zero, lbl);
        self.emit_bailout(lbl, Trap::NIL, pos);
    }

    pub fn test_if_nil(&mut self, reg: Reg) -> Label {
        self.cmp_reg(MachineMode::Ptr, reg, reg);

        let lbl = self.create_label();
        self.jump_if(CondCode::Zero, lbl);

        lbl
    }

    pub fn set(&mut self, dest: Reg, op: CondCode) {
        self.emit_u32(asm::cmp_imm(0, dest, 0, 0));
        self.emit_u32(asm::cset(0, dest, CondCode::NotEqual.into()));
    }

    pub fn cmp_mem(&mut self, mode: MachineMode, mem: Mem, rhs: Reg) {
        let scratch = get_scratch();

        self.load_mem(mode, scratch, mem);
        self.cmp_reg(mode, scratch, rhs);
    }

    pub fn cmp_mem_imm(&mut self, mode: MachineMode, mem: Mem, imm: i32) {
        let (scratch1, scratch2) = get_scratch_registers();

        self.load_mem(mode, scratch1, mem);
        self.load_int_const(mode, scratch2, imm);

        self.cmp_reg(mode, scratch1, scratch2);
    }

    pub fn cmp_reg(&mut self, mode: MachineMode, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::cmp_shreg(size_flag(mode), lhs, rhs, Shift::LSL, 0));
    }

    pub fn test_and_jump_if(&mut self, cond: CondCode, reg: Reg, lbl: Label) {
        assert!(cond == CondCode::Zero || cond == CondCode::NonZero);

        unimplemented!();
    }

    pub fn jump_if(&mut self, cond: CondCode, lbl: Label) {
        unimplemented!();
    }

    pub fn jump(&mut self, lbl: Label) {
        unimplemented!();
    }

    pub fn int_div(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::sdiv(0, dest, lhs, rhs));
    }

    pub fn int_mod(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::sdiv(0, dest, lhs, rhs));
        self.emit_u32(asm::msub(0, dest, dest, rhs, lhs));
    }

    pub fn int_mul(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::mul(0, dest, lhs, rhs));
    }

    pub fn int_add(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::add_reg(0, dest, lhs, rhs));
    }

    pub fn int_sub(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::sub_reg(0, dest, lhs, rhs));
    }

    pub fn int_shl(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::lslv(0, dest, lhs, rhs));
    }

    pub fn int_or(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::orr_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
    }

    pub fn int_and(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::and_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
    }

    pub fn int_xor(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::eor_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
    }

    pub fn check_index_out_of_bounds(&mut self, pos: Position, array: Reg, index: Reg, temp: Reg) {
        self.load_mem(MachineMode::Int32,
                      temp,
                      Mem::Base(array, IntArray::offset_of_length()));
        self.cmp_reg(MachineMode::Int32, index, temp);

        let lbl = self.create_label();
        self.jump_if(CondCode::UnsignedGreaterEq, lbl);
        self.emit_bailout(lbl, Trap::INDEX_OUT_OF_BOUNDS, pos);
    }

    pub fn load_nil(&mut self, dest: Reg) {
        self.emit_u32(add_imm(1, dest, REG_ZERO, 0, 0));
    }

    pub fn load_mem(&mut self, mode: MachineMode, dest: Reg, mem: Mem) {
        match mem {
            Mem::Local(offset) => {
                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, offset);

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::ldrb_ind(dest, REG_FP, scratch, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::ldrw_ind(dest, REG_FP, scratch, LdStExtend::LSL, 0),
                    MachineMode::Ptr =>
                        asm::ldrx_ind(dest, REG_FP, scratch, LdStExtend::LSL, 0),
                };

                self.emit_u32(inst);
            }

            Mem::Base(base, disp) => {
                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, disp);

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::ldrb_ind(dest, base, scratch, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::ldrw_ind(dest, base, scratch, LdStExtend::LSL, 0),
                    MachineMode::Ptr =>
                        asm::ldrx_ind(dest, base, scratch, LdStExtend::LSL, 0),
                };

                self.emit_u32(inst);
            }

            Mem::Index(base, index, scale, disp) => {
                assert!(mode.size() == scale);

                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, disp);
                self.emit_u32(asm::add_reg(1, scratch, scratch, base));

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::ldrb_ind(dest, scratch, index, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::ldrw_ind(dest, scratch, index, LdStExtend::LSL, 1),
                    MachineMode::Ptr =>
                        asm::ldrx_ind(dest, scratch, index, LdStExtend::LSL, 1),
                };

                self.emit_u32(inst);
            }
        }
    }

    pub fn store_mem(&mut self, mode: MachineMode, mem: Mem, src: Reg) {
        match mem {
            Mem::Local(offset) => {
                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, offset);

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::strb_ind(src, REG_FP, scratch, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::strw_ind(src, REG_FP, scratch, LdStExtend::LSL, 0),
                    MachineMode::Ptr =>
                        asm::strx_ind(src, REG_FP, scratch, LdStExtend::LSL, 0),
                };

                self.emit_u32(inst);
            }

            Mem::Base(base, disp) => {
                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, disp);

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::strb_ind(src, base, scratch, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::strw_ind(src, base, scratch, LdStExtend::LSL, 0),
                    MachineMode::Ptr =>
                        asm::strx_ind(src, base, scratch, LdStExtend::LSL, 0),
                };

                self.emit_u32(inst);
            }

            Mem::Index(base, index, scale, disp) => {
                assert!(mode.size() == scale);

                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, disp);
                self.emit_u32(asm::add_reg(1, scratch, scratch, base));

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::strb_ind(src, scratch, index, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::strw_ind(src, scratch, index, LdStExtend::LSL, 1),
                    MachineMode::Ptr =>
                        asm::strx_ind(src, scratch, index, LdStExtend::LSL, 1),
                };

                self.emit_u32(inst);
            }
        }
    }

    pub fn copy_reg(&mut self, mode: MachineMode, dest: Reg, src: Reg) {
        self.emit_u32(orr_shreg(size_flag(mode), dest, REG_ZERO, src, Shift::LSL, 0));
    }

    pub fn load_constpool(&mut self, dest: Reg, disp: i32) {
        self.emit_u32(asm::adr(dest, -disp));
    }

    pub fn call_reg(&mut self, reg: Reg) {
        self.emit_u32(asm::blr(reg));
    }

    pub fn debug(&mut self) {
        unimplemented!();
    }

    pub fn load_int_const(&mut self, mode: MachineMode, dest: Reg, imm: i32) {
        let sf = size_flag(mode);
        let register_size = match mode {
            MachineMode::Int8 => unimplemented!(),
            MachineMode::Int32 => 32,
            MachineMode::Ptr => 64,
        };
        let imm = imm as i64 as u64;

        if fits_movz(imm, register_size) {
            self.emit_u32(movz(sf, dest, imm as u32, shift_movz(imm)));

        } else if fits_movn(imm, register_size) {
            self.emit_u32(movn(sf, dest, imm as u32, shift_movn(imm)));

        } else {
            unimplemented!();
        }
    }

    pub fn load_true(&mut self, dest: Reg) {
        self.emit_u32(movz(0, dest, 1, 0));
    }

    pub fn load_false(&mut self, dest: Reg) {
        self.emit_u32(movz(0, dest, 0, 0));
    }

    pub fn int_neg(&mut self, dest: Reg, src: Reg) {
        self.emit_u32(sub_reg(0, dest, REG_ZERO, src));
    }

    pub fn int_not(&mut self, dest: Reg, src: Reg) {
        self.emit_u32(orn_shreg(0, dest, REG_ZERO, src, Shift::LSL, 0));
    }

    pub fn bool_not(&mut self, dest: Reg, src: Reg) {
        let scratch = get_scratch();

        self.emit_u32(movz(0, scratch, 1, 0));
        self.emit_u32(eor_shreg(0, dest, src, scratch, Shift::LSL, 0));
        self.emit_u32(uxtb(dest, dest));
    }

    pub fn trap(&mut self, trap: Trap) {
        unimplemented!();
    }
}

fn size_flag(mode: MachineMode) -> u32 {
    match mode {
        MachineMode::Int8 |
        MachineMode::Int32 => 0,
        MachineMode::Ptr => 1,
    }
}

fn get_scratch() -> Reg {
    SCRATCH[0]
}

fn get_scratch_registers() -> (Reg, Reg) {
    (SCRATCH[0], SCRATCH[1])
}

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

    #[test]
    fn test_jump_after() {
        let masm = MacroAssembler::new();
        let lbl = masm.create_label();
        masm.jump(lbl);
        masm.emit_label(lbl);

        assert_eq!(vec![0, 0, 0, 0], masm.data());
    }

    #[test]
    fn test_jump_before() {
        let masm = MacroAssembler::new();
        let lbl = masm.create_label();
        masm.emit_label(lbl);
        masm.jump(lbl);

        assert_eq!(vec![0, 0, 0, 0], masm.data());
    }
}<commit_msg>add expected output for jumps<commit_after>use baseline::codegen::CondCode;
use cpu::asm;
use cpu::asm::*;
use cpu::reg::*;
use cpu::{Mem, Reg};
use lexer::position::Position;
use masm::{MacroAssembler, Label};
use mem::ptr_width;
use object::IntArray;
use os::signal::Trap;
use ty::MachineMode;
use vtable::VTable;

impl MacroAssembler {
    pub fn prolog(&mut self, stacksize: i32) {
        self.emit_u32(asm::stp_pre(1, REG_FP, REG_LR, REG_SP, -2));
        self.emit_u32(asm::add_reg(1, REG_FP, REG_SP, REG_ZERO));

        if stacksize > 0 {
            let scratch = get_scratch();
            self.load_int_const(MachineMode::Ptr, scratch, stacksize);
            self.emit_u32(asm::sub_reg(1, REG_SP, REG_SP, scratch));
        }
    }

    pub fn epilog(&mut self, stacksize: i32) {
        if stacksize > 0 {
            let scratch = get_scratch();
            self.load_int_const(MachineMode::Ptr, scratch, stacksize);
            self.emit_u32(asm::add_reg(1, REG_SP, REG_SP, scratch));
        }

        self.emit_u32(asm::add_reg(1, REG_SP, REG_FP, REG_ZERO));
        self.emit_u32(asm::ldp_post(1, REG_FP, REG_LR, REG_SP, 2));
        self.emit_u32(asm::ret());
    }

    pub fn direct_call(&mut self, ptr: *const u8) {
        let disp = self.add_addr(ptr);
        let pos = self.pos() as i32;

        let scratch = get_scratch();

        self.load_constpool(REG_RESULT, disp + pos);
        self.emit_u32(asm::br(scratch));
    }

    pub fn indirect_call(&mut self, index: u32) {
        let obj = REG_PARAMS[0];

        // REG_RESULT = [obj] (load vtable)
        self.load_mem(MachineMode::Ptr, REG_RESULT, Mem::Base(obj, 0));

        // calculate offset of VTable entry
        let disp = VTable::offset_of_method_table() + (index as i32) * ptr_width();

        // load vtable entry
        self.load_mem(MachineMode::Ptr, REG_RESULT, Mem::Base(REG_RESULT, disp));

        // call *REG_RESULT
        self.emit_u32(asm::blr(REG_RESULT));
    }

    pub fn load_array_elem(&mut self, mode: MachineMode, dest: Reg, array: Reg, index: Reg) {
        assert!(mode == MachineMode::Int32);

        self.load_mem(mode,
                      dest,
                      Mem::Index(array, index, mode.size(), IntArray::offset_of_data()));
    }

    pub fn store_array_elem(&mut self, mode: MachineMode, array: Reg, index: Reg, value: Reg) {
        assert!(mode == MachineMode::Int32);

        self.store_mem(MachineMode::Int32,
                       Mem::Index(array, index, 4, IntArray::offset_of_data()),
                       value);
    }

    pub fn test_if_nil_bailout(&mut self, pos: Position, reg: Reg) {
        self.cmp_reg(MachineMode::Ptr, reg, reg);

        let lbl = self.create_label();
        self.jump_if(CondCode::Zero, lbl);
        self.emit_bailout(lbl, Trap::NIL, pos);
    }

    pub fn test_if_nil(&mut self, reg: Reg) -> Label {
        self.cmp_reg(MachineMode::Ptr, reg, reg);

        let lbl = self.create_label();
        self.jump_if(CondCode::Zero, lbl);

        lbl
    }

    pub fn set(&mut self, dest: Reg, op: CondCode) {
        self.emit_u32(asm::cmp_imm(0, dest, 0, 0));
        self.emit_u32(asm::cset(0, dest, CondCode::NotEqual.into()));
    }

    pub fn cmp_mem(&mut self, mode: MachineMode, mem: Mem, rhs: Reg) {
        let scratch = get_scratch();

        self.load_mem(mode, scratch, mem);
        self.cmp_reg(mode, scratch, rhs);
    }

    pub fn cmp_mem_imm(&mut self, mode: MachineMode, mem: Mem, imm: i32) {
        let (scratch1, scratch2) = get_scratch_registers();

        self.load_mem(mode, scratch1, mem);
        self.load_int_const(mode, scratch2, imm);

        self.cmp_reg(mode, scratch1, scratch2);
    }

    pub fn cmp_reg(&mut self, mode: MachineMode, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::cmp_shreg(size_flag(mode), lhs, rhs, Shift::LSL, 0));
    }

    pub fn test_and_jump_if(&mut self, cond: CondCode, reg: Reg, lbl: Label) {
        assert!(cond == CondCode::Zero || cond == CondCode::NonZero);

        unimplemented!();
    }

    pub fn jump_if(&mut self, cond: CondCode, lbl: Label) {
        unimplemented!();
    }

    pub fn jump(&mut self, lbl: Label) {
        unimplemented!();
    }

    pub fn int_div(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::sdiv(0, dest, lhs, rhs));
    }

    pub fn int_mod(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::sdiv(0, dest, lhs, rhs));
        self.emit_u32(asm::msub(0, dest, dest, rhs, lhs));
    }

    pub fn int_mul(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::mul(0, dest, lhs, rhs));
    }

    pub fn int_add(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::add_reg(0, dest, lhs, rhs));
    }

    pub fn int_sub(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::sub_reg(0, dest, lhs, rhs));
    }

    pub fn int_shl(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::lslv(0, dest, lhs, rhs));
    }

    pub fn int_or(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::orr_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
    }

    pub fn int_and(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::and_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
    }

    pub fn int_xor(&mut self, dest: Reg, lhs: Reg, rhs: Reg) {
        self.emit_u32(asm::eor_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
    }

    pub fn check_index_out_of_bounds(&mut self, pos: Position, array: Reg, index: Reg, temp: Reg) {
        self.load_mem(MachineMode::Int32,
                      temp,
                      Mem::Base(array, IntArray::offset_of_length()));
        self.cmp_reg(MachineMode::Int32, index, temp);

        let lbl = self.create_label();
        self.jump_if(CondCode::UnsignedGreaterEq, lbl);
        self.emit_bailout(lbl, Trap::INDEX_OUT_OF_BOUNDS, pos);
    }

    pub fn load_nil(&mut self, dest: Reg) {
        self.emit_u32(add_imm(1, dest, REG_ZERO, 0, 0));
    }

    pub fn load_mem(&mut self, mode: MachineMode, dest: Reg, mem: Mem) {
        match mem {
            Mem::Local(offset) => {
                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, offset);

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::ldrb_ind(dest, REG_FP, scratch, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::ldrw_ind(dest, REG_FP, scratch, LdStExtend::LSL, 0),
                    MachineMode::Ptr =>
                        asm::ldrx_ind(dest, REG_FP, scratch, LdStExtend::LSL, 0),
                };

                self.emit_u32(inst);
            }

            Mem::Base(base, disp) => {
                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, disp);

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::ldrb_ind(dest, base, scratch, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::ldrw_ind(dest, base, scratch, LdStExtend::LSL, 0),
                    MachineMode::Ptr =>
                        asm::ldrx_ind(dest, base, scratch, LdStExtend::LSL, 0),
                };

                self.emit_u32(inst);
            }

            Mem::Index(base, index, scale, disp) => {
                assert!(mode.size() == scale);

                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, disp);
                self.emit_u32(asm::add_reg(1, scratch, scratch, base));

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::ldrb_ind(dest, scratch, index, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::ldrw_ind(dest, scratch, index, LdStExtend::LSL, 1),
                    MachineMode::Ptr =>
                        asm::ldrx_ind(dest, scratch, index, LdStExtend::LSL, 1),
                };

                self.emit_u32(inst);
            }
        }
    }

    pub fn store_mem(&mut self, mode: MachineMode, mem: Mem, src: Reg) {
        match mem {
            Mem::Local(offset) => {
                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, offset);

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::strb_ind(src, REG_FP, scratch, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::strw_ind(src, REG_FP, scratch, LdStExtend::LSL, 0),
                    MachineMode::Ptr =>
                        asm::strx_ind(src, REG_FP, scratch, LdStExtend::LSL, 0),
                };

                self.emit_u32(inst);
            }

            Mem::Base(base, disp) => {
                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, disp);

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::strb_ind(src, base, scratch, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::strw_ind(src, base, scratch, LdStExtend::LSL, 0),
                    MachineMode::Ptr =>
                        asm::strx_ind(src, base, scratch, LdStExtend::LSL, 0),
                };

                self.emit_u32(inst);
            }

            Mem::Index(base, index, scale, disp) => {
                assert!(mode.size() == scale);

                let scratch = get_scratch();
                self.load_int_const(MachineMode::Ptr, scratch, disp);
                self.emit_u32(asm::add_reg(1, scratch, scratch, base));

                let inst = match mode {
                    MachineMode::Int8 =>
                        asm::strb_ind(src, scratch, index, LdStExtend::UXTW, 0),
                    MachineMode::Int32 =>
                        asm::strw_ind(src, scratch, index, LdStExtend::LSL, 1),
                    MachineMode::Ptr =>
                        asm::strx_ind(src, scratch, index, LdStExtend::LSL, 1),
                };

                self.emit_u32(inst);
            }
        }
    }

    pub fn copy_reg(&mut self, mode: MachineMode, dest: Reg, src: Reg) {
        self.emit_u32(orr_shreg(size_flag(mode), dest, REG_ZERO, src, Shift::LSL, 0));
    }

    pub fn load_constpool(&mut self, dest: Reg, disp: i32) {
        self.emit_u32(asm::adr(dest, -disp));
    }

    pub fn call_reg(&mut self, reg: Reg) {
        self.emit_u32(asm::blr(reg));
    }

    pub fn debug(&mut self) {
        unimplemented!();
    }

    pub fn load_int_const(&mut self, mode: MachineMode, dest: Reg, imm: i32) {
        let sf = size_flag(mode);
        let register_size = match mode {
            MachineMode::Int8 => unimplemented!(),
            MachineMode::Int32 => 32,
            MachineMode::Ptr => 64,
        };
        let imm = imm as i64 as u64;

        if fits_movz(imm, register_size) {
            self.emit_u32(movz(sf, dest, imm as u32, shift_movz(imm)));

        } else if fits_movn(imm, register_size) {
            self.emit_u32(movn(sf, dest, imm as u32, shift_movn(imm)));

        } else {
            unimplemented!();
        }
    }

    pub fn load_true(&mut self, dest: Reg) {
        self.emit_u32(movz(0, dest, 1, 0));
    }

    pub fn load_false(&mut self, dest: Reg) {
        self.emit_u32(movz(0, dest, 0, 0));
    }

    pub fn int_neg(&mut self, dest: Reg, src: Reg) {
        self.emit_u32(sub_reg(0, dest, REG_ZERO, src));
    }

    pub fn int_not(&mut self, dest: Reg, src: Reg) {
        self.emit_u32(orn_shreg(0, dest, REG_ZERO, src, Shift::LSL, 0));
    }

    pub fn bool_not(&mut self, dest: Reg, src: Reg) {
        let scratch = get_scratch();

        self.emit_u32(movz(0, scratch, 1, 0));
        self.emit_u32(eor_shreg(0, dest, src, scratch, Shift::LSL, 0));
        self.emit_u32(uxtb(dest, dest));
    }

    pub fn trap(&mut self, trap: Trap) {
        unimplemented!();
    }
}

fn size_flag(mode: MachineMode) -> u32 {
    match mode {
        MachineMode::Int8 |
        MachineMode::Int32 => 0,
        MachineMode::Ptr => 1,
    }
}

fn get_scratch() -> Reg {
    SCRATCH[0]
}

fn get_scratch_registers() -> (Reg, Reg) {
    (SCRATCH[0], SCRATCH[1])
}

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

    #[test]
    fn test_jump_after() {
        let masm = MacroAssembler::new();
        let lbl = masm.create_label();
        masm.jump(lbl);
        masm.emit_label(lbl);

        assert_eq!(vec![0x14, 0, 0, 1], masm.data());
    }

    #[test]
    fn test_jump_before() {
        let masm = MacroAssembler::new();
        let lbl = masm.create_label();
        masm.emit_label(lbl);
        masm.jump(lbl);

        assert_eq!(vec![0x14, 0, 0, 0], masm.data());
    }
}<|endoftext|>


<commit_before>use std::fmt;
use {SdpLine, SdpBandwidth, SdpConnection};
use attribute_type::SdpAttribute;
use error::SdpParserResult;

#[derive(Clone)]
pub struct SdpMediaLine {
    pub media: SdpMediaValue,
    pub port: u32,
    pub proto: SdpProtocolValue,
    pub formats: SdpFormatList,
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpMediaValue {
    Audio,
    Video,
    Application,
}

impl fmt::Display for SdpMediaValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpMediaValue::Audio => "Audio",
            SdpMediaValue::Video => "Video",
            SdpMediaValue::Application => "Application",
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpProtocolValue {
    RtpSavpf,
    UdpTlsRtpSavpf,
    TcpTlsRtpSavpf,
    DtlsSctp,
    UdpDtlsSctp,
    TcpDtlsSctp,
}

impl fmt::Display for SdpProtocolValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpProtocolValue::RtpSavpf => "Rtp/Savpf",
            SdpProtocolValue::UdpTlsRtpSavpf => "Udp/Tls/Rtp/Savpf",
            SdpProtocolValue::TcpTlsRtpSavpf => "Tcp/Tls/Rtp/Savpf",
            SdpProtocolValue::DtlsSctp => "Dtls/Sctp",
            SdpProtocolValue::UdpDtlsSctp => "Udp/Dtls/Sctp",
            SdpProtocolValue::TcpDtlsSctp => "Tcp/Dtls/Sctp",
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone)]
pub enum SdpFormatList {
    Integers { list: Vec<u32> },
    Strings { list: Vec<String> },
}

impl fmt::Display for SdpFormatList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SdpFormatList::Integers { list: ref x } => write!(f, "{:?}", x),
            SdpFormatList::Strings { list: ref x } => write!(f, "{:?}", x),
        }
    }
}

pub struct SdpMedia {
    media: SdpMediaLine,
    information: Option<String>,
    connection: Option<SdpConnection>,
    bandwidth: Vec<SdpBandwidth>,
    key: Option<String>,
    attribute: Vec<SdpAttribute>,
}

impl SdpMedia {
    pub fn new(media: SdpMediaLine) -> SdpMedia {
        SdpMedia {
            media: media,
            information: None,
            connection: None,
            bandwidth: Vec::new(),
            key: None,
            attribute: Vec::new(),
        }
    }

    pub fn get_type(&self) -> &SdpMediaValue {
        &self.media.media
    }

    pub fn get_port(&self) -> u32 {
        self.media.port
    }

    pub fn get_proto(&self) -> &SdpProtocolValue {
        &self.media.proto
    }

    pub fn get_formats(&self) -> &SdpFormatList {
        &self.media.formats
    }

    pub fn has_connection(&self) -> bool {
        self.connection.is_some()
    }

    pub fn has_bandwidth(&self) -> bool {
        !self.bandwidth.is_empty()
    }

    pub fn has_attributes(&self) -> bool {
        !self.attribute.is_empty()
    }

    pub fn add_attribute(&mut self, attr: SdpAttribute) {
        self.attribute.push(attr)
    }

    pub fn add_bandwidth(&mut self, bw: SdpBandwidth) {
        self.bandwidth.push(bw)
    }

    //TODO complain if connection is set already
    pub fn set_connection(&mut self, c: SdpConnection) {
        self.connection = Some(c);
    }

    //TODO complain if information is set already
    pub fn set_information(&mut self, i: String) {
        self.information = Some(i);
    }

    //TODO complain if key is set already
    pub fn set_key(&mut self, k: String) {
        self.key = Some(k);
    }
}
fn parse_media_token(value: &str) -> Result<SdpMediaValue, SdpParserResult> {
    Ok(match value.to_lowercase().as_ref() {
           "audio" => SdpMediaValue::Audio,
           "video" => SdpMediaValue::Video,
           "application" => SdpMediaValue::Application,
           _ => {
               return Err(SdpParserResult::ParserUnsupported {
                              message: "unsupported media value".to_string(),
                              line: value.to_string(),
                          })
           }
       })
}

#[test]
fn test_parse_media_token() {
    let audio = parse_media_token("audio");
    assert!(audio.is_ok());
    assert_eq!(audio.unwrap(), SdpMediaValue::Audio);
    let video = parse_media_token("VIDEO");
    assert!(video.is_ok());
    assert_eq!(video.unwrap(), SdpMediaValue::Video);
    let app = parse_media_token("aPplIcatIOn");
    assert!(app.is_ok());
    assert_eq!(app.unwrap(), SdpMediaValue::Application);

    assert!(parse_media_token("").is_err());
    assert!(parse_media_token("foobar").is_err());
}

fn parse_protocol_token(value: &str) -> Result<SdpProtocolValue, SdpParserResult> {
    Ok(match value.to_uppercase().as_ref() {
           "RTP/SAVPF" => SdpProtocolValue::RtpSavpf,
           "UDP/TLS/RTP/SAVPF" => SdpProtocolValue::UdpTlsRtpSavpf,
           "TCP/TLS/RTP/SAVPF" => SdpProtocolValue::TcpTlsRtpSavpf,
           "DTLS/SCTP" => SdpProtocolValue::DtlsSctp,
           "UDP/DTLS/SCTP" => SdpProtocolValue::UdpDtlsSctp,
           "TCP/DTLS/SCTP" => SdpProtocolValue::TcpDtlsSctp,
           _ => {
               return Err(SdpParserResult::ParserUnsupported {
                              message: "unsupported protocol value".to_string(),
                              line: value.to_string(),
                          })
           }
       })
}

#[test]
fn test_parse_protocol_token() {
    let rtps = parse_protocol_token("rtp/savpf");
    assert!(rtps.is_ok());
    assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpSavpf);
    let udps = parse_protocol_token("udp/tls/rtp/savpf");
    assert!(udps.is_ok());
    assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavpf);
    let tcps = parse_protocol_token("TCP/tls/rtp/savpf");
    assert!(tcps.is_ok());
    assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpTlsRtpSavpf);
    let dtls = parse_protocol_token("dtLs/ScTP");
    assert!(dtls.is_ok());
    assert_eq!(dtls.unwrap(), SdpProtocolValue::DtlsSctp);
    let usctp = parse_protocol_token("udp/DTLS/sctp");
    assert!(usctp.is_ok());
    assert_eq!(usctp.unwrap(), SdpProtocolValue::UdpDtlsSctp);
    let tsctp = parse_protocol_token("tcp/dtls/SCTP");
    assert!(tsctp.is_ok());
    assert_eq!(tsctp.unwrap(), SdpProtocolValue::TcpDtlsSctp);

    assert!(parse_protocol_token("").is_err());
    assert!(parse_protocol_token("foobar").is_err());
}

pub fn parse_media(value: &str) -> Result<SdpLine, SdpParserResult> {
    let mv: Vec<&str> = value.split_whitespace().collect();
    if mv.len() < 4 {
        return Err(SdpParserResult::ParserLineError {
                       message: "media attribute must have at least four tokens".to_string(),
                       line: value.to_string(),
                   });
    }
    let media = try!(parse_media_token(mv[0]));
    let port = try!(mv[1].parse::<u32>());
    if port > 65535 {
        return Err(SdpParserResult::ParserLineError {
                       message: "media port token is too big".to_string(),
                       line: value.to_string(),
                   });
    }
    let proto = try!(parse_protocol_token(mv[2]));
    let fmt_slice: &[&str] = &mv[3..];
    let fmt = match media {
        SdpMediaValue::Audio | SdpMediaValue::Video => {
            let mut fmt_vec: Vec<u32> = vec![];
            for num in fmt_slice {
                let fmt_num = try!(num.parse::<u32>());
                match fmt_num {
                    0  |  // PCMU
                    8  |  // PCMA
                    9  |  // G722
                    13 |  // Comfort Noise
                    96 ... 127 => (),  // dynamic range
                    _ => return Err(SdpParserResult::ParserLineError {
                          message: "format number in media line is out of range".to_string(),
                          line: value.to_string() }),
                };
                fmt_vec.push(fmt_num);
            }
            SdpFormatList::Integers { list: fmt_vec }
        }
        SdpMediaValue::Application => {
            let mut fmt_vec: Vec<String> = vec![];
            // TODO enforce length == 1 and content 'webrtc-datachannel' only?
            for token in fmt_slice {
                fmt_vec.push(String::from(*token));
            }
            SdpFormatList::Strings { list: fmt_vec }
        }
    };
    let m = SdpMediaLine {
        media: media,
        port: port,
        proto: proto,
        formats: fmt,
    };
    println!("media: {}, {}, {}, {}", m.media, m.port, m.proto, m.formats);
    Ok(SdpLine::Media { value: m })
}

#[test]
fn test_media_works() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109").is_ok());
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF 126").is_ok());
    assert!(parse_media("application 9 DTLS/SCTP 5000").is_ok());
    assert!(parse_media("application 9 UDP/DTLS/SCTP webrtc-datachannel").is_ok());

    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8").is_ok());
    assert!(parse_media("audio 0 UDP/TLS/RTP/SAVPF 8").is_ok());
}

#[test]
fn test_media_missing_token() {
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF").is_err());
}

#[test]
fn test_media_invalid_port_number() {
    assert!(parse_media("video 75123 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_type() {
    assert!(parse_media("invalid 9 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_transport() {
    assert!(parse_media("audio 9 invalid/invalid 8").is_err());
}

#[test]
fn test_media_invalid_payload() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 300").is_err());
}

pub fn parse_media_vector(lines: &[SdpLine]) -> Result<Vec<SdpMedia>, SdpParserResult> {
    let mut media_sections: Vec<SdpMedia> = Vec::new();
    let mut sdp_media = match lines[0] {
        SdpLine::Media { value: ref v } => SdpMedia::new(v.clone()),
        _ => {
            return Err(SdpParserResult::ParserSequence {
                           message: "first line in media section needs to be a media line"
                               .to_string(),
                           line: None,
                       })
        }
    };
    for line in lines.iter().skip(1) {
        match *line {
            SdpLine::Information { value: ref v } => sdp_media.set_information(v.clone()),
            SdpLine::Connection { value: ref v } => sdp_media.set_connection(v.clone()),
            SdpLine::Bandwidth { value: ref v } => {
                sdp_media.add_bandwidth(v.clone());
            }
            SdpLine::Key { value: ref v } => sdp_media.set_key(v.clone()),
            SdpLine::Attribute { value: ref v } => {
                sdp_media.add_attribute(v.clone());
            }
            SdpLine::Media { value: ref v } => {
                media_sections.push(sdp_media);
                sdp_media = SdpMedia::new(v.clone());
            }

            SdpLine::Email { .. } |
            SdpLine::Phone { .. } |
            SdpLine::Origin { .. } |
            SdpLine::Repeat { .. } |
            SdpLine::Session { .. } |
            SdpLine::Timing { .. } |
            SdpLine::Uri { .. } |
            SdpLine::Version { .. } |
            SdpLine::Zone { .. } => {
                return Err(SdpParserResult::ParserSequence {
                               message: "invalid type in media section".to_string(),
                               line: None,
                           })
            }
        };
    }
    media_sections.push(sdp_media);
    Ok(media_sections)
}
// TODO add unit tests for parse_media_vector
<commit_msg>use new struct init<commit_after>use std::fmt;
use {SdpLine, SdpBandwidth, SdpConnection};
use attribute_type::SdpAttribute;
use error::SdpParserResult;

#[derive(Clone)]
pub struct SdpMediaLine {
    pub media: SdpMediaValue,
    pub port: u32,
    pub proto: SdpProtocolValue,
    pub formats: SdpFormatList,
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpMediaValue {
    Audio,
    Video,
    Application,
}

impl fmt::Display for SdpMediaValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpMediaValue::Audio => "Audio",
            SdpMediaValue::Video => "Video",
            SdpMediaValue::Application => "Application",
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpProtocolValue {
    RtpSavpf,
    UdpTlsRtpSavpf,
    TcpTlsRtpSavpf,
    DtlsSctp,
    UdpDtlsSctp,
    TcpDtlsSctp,
}

impl fmt::Display for SdpProtocolValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpProtocolValue::RtpSavpf => "Rtp/Savpf",
            SdpProtocolValue::UdpTlsRtpSavpf => "Udp/Tls/Rtp/Savpf",
            SdpProtocolValue::TcpTlsRtpSavpf => "Tcp/Tls/Rtp/Savpf",
            SdpProtocolValue::DtlsSctp => "Dtls/Sctp",
            SdpProtocolValue::UdpDtlsSctp => "Udp/Dtls/Sctp",
            SdpProtocolValue::TcpDtlsSctp => "Tcp/Dtls/Sctp",
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone)]
pub enum SdpFormatList {
    Integers { list: Vec<u32> },
    Strings { list: Vec<String> },
}

impl fmt::Display for SdpFormatList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SdpFormatList::Integers { list: ref x } => write!(f, "{:?}", x),
            SdpFormatList::Strings { list: ref x } => write!(f, "{:?}", x),
        }
    }
}

pub struct SdpMedia {
    media: SdpMediaLine,
    information: Option<String>,
    connection: Option<SdpConnection>,
    bandwidth: Vec<SdpBandwidth>,
    key: Option<String>,
    attribute: Vec<SdpAttribute>,
}

impl SdpMedia {
    pub fn new(media: SdpMediaLine) -> SdpMedia {
        SdpMedia {
            media: media,
            information: None,
            connection: None,
            bandwidth: Vec::new(),
            key: None,
            attribute: Vec::new(),
        }
    }

    pub fn get_type(&self) -> &SdpMediaValue {
        &self.media.media
    }

    pub fn get_port(&self) -> u32 {
        self.media.port
    }

    pub fn get_proto(&self) -> &SdpProtocolValue {
        &self.media.proto
    }

    pub fn get_formats(&self) -> &SdpFormatList {
        &self.media.formats
    }

    pub fn has_connection(&self) -> bool {
        self.connection.is_some()
    }

    pub fn has_bandwidth(&self) -> bool {
        !self.bandwidth.is_empty()
    }

    pub fn has_attributes(&self) -> bool {
        !self.attribute.is_empty()
    }

    pub fn add_attribute(&mut self, attr: SdpAttribute) {
        self.attribute.push(attr)
    }

    pub fn add_bandwidth(&mut self, bw: SdpBandwidth) {
        self.bandwidth.push(bw)
    }

    //TODO complain if connection is set already
    pub fn set_connection(&mut self, c: SdpConnection) {
        self.connection = Some(c);
    }

    //TODO complain if information is set already
    pub fn set_information(&mut self, i: String) {
        self.information = Some(i);
    }

    //TODO complain if key is set already
    pub fn set_key(&mut self, k: String) {
        self.key = Some(k);
    }
}
fn parse_media_token(value: &str) -> Result<SdpMediaValue, SdpParserResult> {
    Ok(match value.to_lowercase().as_ref() {
           "audio" => SdpMediaValue::Audio,
           "video" => SdpMediaValue::Video,
           "application" => SdpMediaValue::Application,
           _ => {
               return Err(SdpParserResult::ParserUnsupported {
                              message: "unsupported media value".to_string(),
                              line: value.to_string(),
                          })
           }
       })
}

#[test]
fn test_parse_media_token() {
    let audio = parse_media_token("audio");
    assert!(audio.is_ok());
    assert_eq!(audio.unwrap(), SdpMediaValue::Audio);
    let video = parse_media_token("VIDEO");
    assert!(video.is_ok());
    assert_eq!(video.unwrap(), SdpMediaValue::Video);
    let app = parse_media_token("aPplIcatIOn");
    assert!(app.is_ok());
    assert_eq!(app.unwrap(), SdpMediaValue::Application);

    assert!(parse_media_token("").is_err());
    assert!(parse_media_token("foobar").is_err());
}

fn parse_protocol_token(value: &str) -> Result<SdpProtocolValue, SdpParserResult> {
    Ok(match value.to_uppercase().as_ref() {
           "RTP/SAVPF" => SdpProtocolValue::RtpSavpf,
           "UDP/TLS/RTP/SAVPF" => SdpProtocolValue::UdpTlsRtpSavpf,
           "TCP/TLS/RTP/SAVPF" => SdpProtocolValue::TcpTlsRtpSavpf,
           "DTLS/SCTP" => SdpProtocolValue::DtlsSctp,
           "UDP/DTLS/SCTP" => SdpProtocolValue::UdpDtlsSctp,
           "TCP/DTLS/SCTP" => SdpProtocolValue::TcpDtlsSctp,
           _ => {
               return Err(SdpParserResult::ParserUnsupported {
                              message: "unsupported protocol value".to_string(),
                              line: value.to_string(),
                          })
           }
       })
}

#[test]
fn test_parse_protocol_token() {
    let rtps = parse_protocol_token("rtp/savpf");
    assert!(rtps.is_ok());
    assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpSavpf);
    let udps = parse_protocol_token("udp/tls/rtp/savpf");
    assert!(udps.is_ok());
    assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavpf);
    let tcps = parse_protocol_token("TCP/tls/rtp/savpf");
    assert!(tcps.is_ok());
    assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpTlsRtpSavpf);
    let dtls = parse_protocol_token("dtLs/ScTP");
    assert!(dtls.is_ok());
    assert_eq!(dtls.unwrap(), SdpProtocolValue::DtlsSctp);
    let usctp = parse_protocol_token("udp/DTLS/sctp");
    assert!(usctp.is_ok());
    assert_eq!(usctp.unwrap(), SdpProtocolValue::UdpDtlsSctp);
    let tsctp = parse_protocol_token("tcp/dtls/SCTP");
    assert!(tsctp.is_ok());
    assert_eq!(tsctp.unwrap(), SdpProtocolValue::TcpDtlsSctp);

    assert!(parse_protocol_token("").is_err());
    assert!(parse_protocol_token("foobar").is_err());
}

pub fn parse_media(value: &str) -> Result<SdpLine, SdpParserResult> {
    let mv: Vec<&str> = value.split_whitespace().collect();
    if mv.len() < 4 {
        return Err(SdpParserResult::ParserLineError {
                       message: "media attribute must have at least four tokens".to_string(),
                       line: value.to_string(),
                   });
    }
    let media = try!(parse_media_token(mv[0]));
    let port = try!(mv[1].parse::<u32>());
    if port > 65535 {
        return Err(SdpParserResult::ParserLineError {
                       message: "media port token is too big".to_string(),
                       line: value.to_string(),
                   });
    }
    let proto = try!(parse_protocol_token(mv[2]));
    let fmt_slice: &[&str] = &mv[3..];
    let formats = match media {
        SdpMediaValue::Audio | SdpMediaValue::Video => {
            let mut fmt_vec: Vec<u32> = vec![];
            for num in fmt_slice {
                let fmt_num = try!(num.parse::<u32>());
                match fmt_num {
                    0  |  // PCMU
                    8  |  // PCMA
                    9  |  // G722
                    13 |  // Comfort Noise
                    96 ... 127 => (),  // dynamic range
                    _ => return Err(SdpParserResult::ParserLineError {
                          message: "format number in media line is out of range".to_string(),
                          line: value.to_string() }),
                };
                fmt_vec.push(fmt_num);
            }
            SdpFormatList::Integers { list: fmt_vec }
        }
        SdpMediaValue::Application => {
            let mut fmt_vec: Vec<String> = vec![];
            // TODO enforce length == 1 and content 'webrtc-datachannel' only?
            for token in fmt_slice {
                fmt_vec.push(String::from(*token));
            }
            SdpFormatList::Strings { list: fmt_vec }
        }
    };
    let m = SdpMediaLine {
        media,
        port,
        proto,
        formats,
    };
    println!("media: {}, {}, {}, {}", m.media, m.port, m.proto, m.formats);
    Ok(SdpLine::Media { value: m })
}

#[test]
fn test_media_works() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109").is_ok());
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF 126").is_ok());
    assert!(parse_media("application 9 DTLS/SCTP 5000").is_ok());
    assert!(parse_media("application 9 UDP/DTLS/SCTP webrtc-datachannel").is_ok());

    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8").is_ok());
    assert!(parse_media("audio 0 UDP/TLS/RTP/SAVPF 8").is_ok());
}

#[test]
fn test_media_missing_token() {
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF").is_err());
}

#[test]
fn test_media_invalid_port_number() {
    assert!(parse_media("video 75123 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_type() {
    assert!(parse_media("invalid 9 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_transport() {
    assert!(parse_media("audio 9 invalid/invalid 8").is_err());
}

#[test]
fn test_media_invalid_payload() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 300").is_err());
}

pub fn parse_media_vector(lines: &[SdpLine]) -> Result<Vec<SdpMedia>, SdpParserResult> {
    let mut media_sections: Vec<SdpMedia> = Vec::new();
    let mut sdp_media = match lines[0] {
        SdpLine::Media { value: ref v } => SdpMedia::new(v.clone()),
        _ => {
            return Err(SdpParserResult::ParserSequence {
                           message: "first line in media section needs to be a media line"
                               .to_string(),
                           line: None,
                       })
        }
    };
    for line in lines.iter().skip(1) {
        match *line {
            SdpLine::Information { value: ref v } => sdp_media.set_information(v.clone()),
            SdpLine::Connection { value: ref v } => sdp_media.set_connection(v.clone()),
            SdpLine::Bandwidth { value: ref v } => {
                sdp_media.add_bandwidth(v.clone());
            }
            SdpLine::Key { value: ref v } => sdp_media.set_key(v.clone()),
            SdpLine::Attribute { value: ref v } => {
                sdp_media.add_attribute(v.clone());
            }
            SdpLine::Media { value: ref v } => {
                media_sections.push(sdp_media);
                sdp_media = SdpMedia::new(v.clone());
            }

            SdpLine::Email { .. } |
            SdpLine::Phone { .. } |
            SdpLine::Origin { .. } |
            SdpLine::Repeat { .. } |
            SdpLine::Session { .. } |
            SdpLine::Timing { .. } |
            SdpLine::Uri { .. } |
            SdpLine::Version { .. } |
            SdpLine::Zone { .. } => {
                return Err(SdpParserResult::ParserSequence {
                               message: "invalid type in media section".to_string(),
                               line: None,
                           })
            }
        };
    }
    media_sections.push(sdp_media);
    Ok(media_sections)
}
// TODO add unit tests for parse_media_vector
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
use alloc::buddy;
pub use arch::memory::PAddr;
pub mod vaddr_impls;
pub use self::vaddr_impls::*;

/// A virtual address is a machine-sized unsigned integer
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct VAddr(usize);

impl VAddr {
    #[inline] pub const fn from_usize(u: usize) -> Self {
        VAddr(u)
    }
    #[inline] pub const fn as_usize(&self) -> usize {
        self.0
    }

    /// Calculate the index in the PML4 table corresponding to this address.
    #[inline] pub fn pml4_index(&self) -> usize {
        (self >> 39) & 0b111111111
    }

    /// Calculate the index in the PDPT table corresponding to this address.
    #[inline] pub fn pdpt_index(&self) -> usize {
        (self >> 30) & 0b111111111
    }

    /// Calculate the index in the PD table corresponding to this address.
    #[inline] pub fn pd_index(&self) -> usize {
        (self >> 21) & 0b111111111
    }

    /// Calculate the index in the PT table corresponding to this address.
    #[inline] pub fn pt_index(&self) -> usize {
        (self >> 12) & 0b111111111
    }
}

extern {
    static mut HEAP_BASE: u8;
    static mut HEAP_TOP: u8;
}

#[inline] pub fn heap_base_addr() -> usize {
    unsafe { (&mut HEAP_BASE as *mut _) as usize }
}

#[inline] pub fn heap_top_addr() -> usize {
    unsafe { (&mut HEAP_TOP as *mut _) as usize }
}

static mut KERNEL_FREE_LISTS: [buddy::FreeList; 19]
    // TODO: I really wish there was a less awful way to do this...
    = [ buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      , buddy::FreeList::new()
      ];

pub unsafe fn init_heap() {
    let heap_base_ptr
        = &mut HEAP_BASE as *mut _;
    let heap_size
        = (&mut HEAP_TOP as *mut _) as usize - heap_base_ptr as usize;
    buddy::system::init_heap( heap_base_ptr
                            , &mut KERNEL_FREE_LISTS
                            , heap_size);
}
<commit_msg>[kernel] Add function for converting *mut u8 to VAddr<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
use alloc::buddy;

pub use arch::memory::PAddr;
pub mod vaddr_impls;
pub use self::vaddr_impls::*;

/// A virtual address is a machine-sized unsigned integer
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct VAddr(usize);

impl VAddr {
    #[inline] pub fn from_ptr(ptr: *mut u8) -> Self {
        VAddr(ptr as usize)
    }
    #[inline] pub const fn from_usize(u: usize) -> Self {
        VAddr(u)
    }
    #[inline] pub const fn as_usize(&self) -> usize {
        self.0
    }

    /// Calculate the index in the PML4 table corresponding to this address.
    #[inline] pub fn pml4_index(&self) -> usize {
        (self >> 39) & 0b111111111
    }

    /// Calculate the index in the PDPT table corresponding to this address.
    #[inline] pub fn pdpt_index(&self) -> usize {
        (self >> 30) & 0b111111111
    }

    /// Calculate the index in the PD table corresponding to this address.
    #[inline] pub fn pd_index(&self) -> usize {
        (self >> 21) & 0b111111111
    }

    /// Calculate the index in the PT table corresponding to this address.
    #[inline] pub fn pt_index(&self) -> usize {
        (self >> 12) & 0b111111111
    }
}

extern {
    static mut HEAP_BASE: u8;
    static mut HEAP_TOP: u8;
}

#[inline] pub fn heap_base_addr() -> usize {
    unsafe { (&mut HEAP_BASE as *mut _) as usize }
}

#[inline] pub fn heap_top_addr() -> usize {
    unsafe { (&mut HEAP_TOP as *mut _) as usize }
}

pub unsafe fn init_heap() {
    let heap_base_ptr
        = &mut HEAP_BASE as *mut _;
    let heap_size
        = (&mut HEAP_TOP as *mut _) as usize - heap_base_ptr as usize;
    buddy::system::init_heap( heap_base_ptr, heap_size);
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use utils;
pub struct Memory
{
    ram: [u8;0x10000],
    rom: [u8;0x10000],
    // bank switching flags
    basic_on: bool,
    chargen_on: bool,
    kernal_on: bool,
}

impl Memory
{
    pub fn new() -> Memory
    {
        let mut memory = Memory
        {
            ram: [0;0x10000],   // 64k
            rom: [0;0x10000],   // store ROM data in 64k array so it's easier to address it with bank switching
            basic_on: true,
            chargen_on: true,
            kernal_on: true
        };

        // load basic
        let mut start_addr: u32 = 0xA000;
        let basic = utils::open_file("rom/basic.rom");

        for (i,addr) in (start_addr..0xC000).enumerate()
        {
            memory.rom[addr as usize] = basic[i as usize];
        }

        // load chargen
        start_addr = 0xD000;
        let chargen = utils::open_file("rom/chargen.rom");

        for (i,addr) in (start_addr..0xE000).enumerate()
        {
            memory.rom[addr as usize] = chargen[i as usize];
        }

        // load kernal
        start_addr = 0xE000;
        let kernal = utils::open_file("rom/kernal.rom");

        for (i,addr) in (start_addr..0x10000).enumerate()
        {
            memory.rom[addr as usize] = kernal[i as usize];
        }

        memory
    }

    pub fn get_bank(&mut self, addr: u16) -> &mut [u8]
    {
        match addr
        {
            0x0000...0x9FFF => &mut self.ram[0..0x10000],
            0xA000...0xCFFF => if self.basic_on   { &mut self.rom[0..0x10000] } else { &mut self.ram[0..0x10000] },
            0xD000...0xDFFF => if self.chargen_on { &mut self.rom[0..0x10000] } else { &mut self.ram[0..0x10000] },
            0xE000...0xFFFF => if self.kernal_on  { &mut self.rom[0..0x10000] } else { &mut self.ram[0..0x10000] },
            _ => panic!("Address out of memory range")
        }
    }

    pub fn reset(&mut self)
    {
        self.write_byte(0x0000, 0xFF);
        self.write_byte(0x0001, 0x07); // enable kernal, chargen and basic access

        self.update_bank_flags();
    }

    fn update_bank_flags(&mut self)
    {
        let latch = self.ram[0x0001];
        self.basic_on   = (latch & 0x0B) != 0 || (latch & 0x0F) != 0;
        self.chargen_on = (latch & 0x09) != 0 || (latch & 0x0A) != 0 || (latch & 0x0B) != 0 || (latch == 0x02) || (latch == 0x03);
        self.kernal_on  = (latch & 0x0F) != 0 || (latch & 0x0E) != 0 || (latch & 0x0B) != 0 || (latch &0x0A) != 0 || (latch == 0x02) || (latch == 0x03) || (latch == 0x07) || (latch == 0x06);
    }

    // Write a byte to memory
    pub fn write_byte(&mut self, addr: u16, value: u8)
    {
        // parentheses to avoid borrowing issues with changing the flags
        {
            let bank = self.get_bank(addr);
            bank[addr as usize] = value;
        }

        // update the bank switching flags here, since they can only change on memory write
        // latch byte changed - update bank switching flags
        if addr == 0x0001 { self.update_bank_flags(); }
    }

    // Read a byte from memory
    pub fn read_byte(&mut self, addr: u16) -> u8
    {
        let bank = self.get_bank(addr);
        bank[addr as usize]
    }

    // Read a word from memory (stored in little endian)
    pub fn read_word_le(&mut self, addr: u16) -> u16
    {
        let bank = self.get_bank(addr);
        let value_be: u16 = ((bank[addr as usize] as u16) << 8 & 0xFF00) |
                            ((bank[(addr + 0x0001) as usize] as u16) & 0x00FF);

        let value_le: u16 = ((value_be << 8) & 0xFF00) | ((value_be >> 8) & 0x00FF);
        value_le
    }

    // Read a word from memory (stored in big endian: swap low<->high)
    pub fn read_word_be(&mut self, addr: u16) -> u16
    {
        let bank = self.get_bank(addr);
        let value_le: u16 = ((bank[addr as usize] as u16) << 8 & 0xFF00) |
                            ((bank[(addr + 0x0001) as usize] as u16) & 0x00FF);
        value_le
    }

    // Write word in little endian format (low/high)
    pub fn write_word_le(&mut self, addr: u16, value: u16)
    {
        let bank = self.get_bank(addr);
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        bank[addr as usize] = value_le_lo;
        bank[(addr + 0x0001) as usize] = value_le_hi;
    }

    // Write word in big endian format (high/low)
    pub fn write_word_be(&mut self, addr: u16, value: u16)
    {
        let bank = self.get_bank(addr);
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        bank[addr as usize] = value_le_hi;
        bank[(addr + 0x0001) as usize] = value_le_lo;
    }
}

<commit_msg>all memory latch configurations handled<commit_after>#![allow(dead_code)]
use utils;
pub struct Memory
{
    ram: [u8;0x10000],
    rom: [u8;0x10000],
    // bank switching flags
    basic_on: bool,
    chargen_on: bool,
    io_on: bool,
    kernal_on: bool,
    cart_lo_on: bool, // cart flag - unused for now
    cart_hi_on: bool  // cart flag - unused for now
}

impl Memory
{
    pub fn new() -> Memory
    {
        let mut memory = Memory
        {
            ram: [0;0x10000],   // 64k
            rom: [0;0x10000],   // store ROM data in 64k array so it's easier to address it with bank switching
            basic_on: false,
            chargen_on: false,
            io_on: false,
            kernal_on: false,
            cart_lo_on: false, // unused for now
            cart_hi_on: false, // unused for now
        };

        // load basic
        let mut start_addr: u32 = 0xA000;
        let basic = utils::open_file("rom/basic.rom");

        for (i,addr) in (start_addr..0xC000).enumerate()
        {
            memory.rom[addr as usize] = basic[i as usize];
        }

        // load chargen
        start_addr = 0xD000;
        let chargen = utils::open_file("rom/chargen.rom");

        for (i,addr) in (start_addr..0xE000).enumerate()
        {
            memory.rom[addr as usize] = chargen[i as usize];
        }

        // load kernal
        start_addr = 0xE000;
        let kernal = utils::open_file("rom/kernal.rom");

        for (i,addr) in (start_addr..0x10000).enumerate()
        {
            memory.rom[addr as usize] = kernal[i as usize];
        }

        memory
    }

    pub fn get_bank(&mut self, addr: u16) -> &mut [u8]
    {
        match addr
        {
            0x0000...0x9FFF => &mut self.ram[0..0x10000],
            0xA000...0xCFFF => if self.basic_on   { &mut self.rom[0..0x10000] } else { &mut self.ram[0..0x10000] },
            0xD000...0xDFFF => if self.chargen_on { &mut self.rom[0..0x10000] } else { &mut self.ram[0..0x10000] },
            0xE000...0xFFFF => if self.kernal_on  { &mut self.rom[0..0x10000] } else { &mut self.ram[0..0x10000] },
            _ => panic!("Address out of memory range")
        }
    }

    pub fn reset(&mut self)
    {
        self.write_byte(0x0000, 0xFF);
        self.write_byte(0x0001, 0x07); // enable kernal, chargen and basic access
    }

    fn update_bank_flags(&mut self)
    {
        // latch state is determined by 5 least significant bits from this location
        let latch = self.ram[0x0001] & 0x1F;

        // default to RAM only
        self.basic_on = false;
        self.chargen_on = false;
        self.io_on = false;
        self.kernal_on = false;
        self.cart_lo_on = false;
        self.cart_hi_on = false;

        match latch
        {
            0x02 => {
                self.chargen_on = true;
                self.kernal_on = true;
                self.cart_hi_on = true;
            },
            0x03 => {
                self.chargen_on = true;
                self.kernal_on = true;
                self.cart_lo_on = true;
                self.cart_hi_on = true;
            }
            0x05 | 0x0D | 0x1D => {
                self.io_on = true;
            }
            0x06 => {
                self.cart_hi_on = true;
                self.io_on = true;
            },
            0x07 => {
                self.cart_lo_on = true;
                self.cart_hi_on = true;
                self.io_on = true;
                self.kernal_on = true;
            },
            0x0B => {
                self.cart_lo_on = true;
                self.basic_on = true;
                self.chargen_on = true;
                self.kernal_on = true;
            },
            0x0F => {
                self.cart_lo_on = true;
                self.basic_on = true;
                self.io_on = true;
                self.kernal_on = true;
            },
            0x10...0x17 => {
                self.cart_lo_on = true;
                self.io_on = true;
                self.cart_hi_on = true;
            },
            0x19 | 0x09 => {
                self.chargen_on = true;
            },
            0x1A | 0x0A => {
                self.chargen_on = true;
                self.kernal_on = true;
            },
            0x1B => {
                self.basic_on = true;
                self.chargen_on = true;
                self.kernal_on = true;
            },
            0xE | 0x1E => {
                self.io_on = true;
                self.kernal_on = true;
            },
            0x1F => {
                self.basic_on = true;
                self.io_on = true;
                self.kernal_on = true;
            },
            _ => ()
        }
    }

    // Write a byte to memory
    pub fn write_byte(&mut self, addr: u16, value: u8)
    {
        // parentheses to avoid borrowing issues with changing the flags
        {
            let bank = self.get_bank(addr);
            bank[addr as usize] = value;
        }

        // update the bank switching flags here, since they can only change on memory write
        // latch byte changed - update bank switching flags
        if addr == 0x0001 { self.update_bank_flags(); }
    }

    // Read a byte from memory
    pub fn read_byte(&mut self, addr: u16) -> u8
    {
        let bank = self.get_bank(addr);
        bank[addr as usize]
    }

    // Read a word from memory (stored in little endian)
    pub fn read_word_le(&mut self, addr: u16) -> u16
    {
        let bank = self.get_bank(addr);
        let value_be: u16 = ((bank[addr as usize] as u16) << 8 & 0xFF00) |
                            ((bank[(addr + 0x0001) as usize] as u16) & 0x00FF);

        let value_le: u16 = ((value_be << 8) & 0xFF00) | ((value_be >> 8) & 0x00FF);
        value_le
    }

    // Read a word from memory (stored in big endian: swap low<->high)
    pub fn read_word_be(&mut self, addr: u16) -> u16
    {
        let bank = self.get_bank(addr);
        let value_le: u16 = ((bank[addr as usize] as u16) << 8 & 0xFF00) |
                            ((bank[(addr + 0x0001) as usize] as u16) & 0x00FF);
        value_le
    }

    // Write word in little endian format (low/high)
    pub fn write_word_le(&mut self, addr: u16, value: u16)
    {
        let bank = self.get_bank(addr);
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        bank[addr as usize] = value_le_lo;
        bank[(addr + 0x0001) as usize] = value_le_hi;
    }

    // Write word in big endian format (high/low)
    pub fn write_word_be(&mut self, addr: u16, value: u16)
    {
        let bank = self.get_bank(addr);
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        bank[addr as usize] = value_le_hi;
        bank[(addr + 0x0001) as usize] = value_le_lo;
    }
}

<|endoftext|>


<commit_before>extern crate futures;
extern crate tokio_core;

#[macro_use]
extern crate reql;
extern crate reql_types;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

use futures::stream::Stream;
use tokio_core::reactor::Core;
use reql::{Client, Document, Run};

/**
 * rethinkdb changelog example
 * After running, access your admin panel at localhost:8080
 *
 * setup a "test" database, with a "test" table, and run:
 *
 * r.db('test').table('test').insert({ test: 1 })
 *
 */

#[derive(Debug, Serialize, Deserialize)]
struct TestItem {
    test: i32
}

#[derive(Debug, Serialize, Deserialize)]
struct Change {
    // Upon deserialisation:
    // We'll change the rethinkdb change "type" to avoid  naming issues
    #[serde(rename(deserialize = "type"))]
    action: String,
    new_val: TestItem,
    old_val: TestItem,
}

fn main()
{
    // Create a new ReQL client
    let r = Client::new();

    // Create an even loop
    let core = Core::new().unwrap();

    // Create a connection pool
    let conn = r.connect(&core.handle()).unwrap();

    // Run the query
    let query = r.db("test")
        .table("test")
        .changes()

    // We want rethinkdb to inform us of the change type
        .with_args(args!({
            include_types: true
        }))
        .run::<Change>(conn)
        .unwrap();

    // Process the results
    let stati = query.and_then(|change| {
        match change {
            // The server returned the response we were expecting,
            // and deserialized the data into our Change structure
            Some(Document::Expected(change)) => {
                // Extract the change type
                print!("{} action received\n\t=> ", change.action);

                // Match the change type
                match change.action.as_ref() {
                    "add" => println!("{:?}", change.new_val),
                    "remove" => println!("{:?}", change.old_val),
                    "change" => println!("from {:?} to {:?}", change.old_val, change.new_val),

                    _ => println!("{:?}", change)
                }
            }
            // We got a response alright, but it wasn't the one we were
            // expecting plus it's not an error either, otherwise it would
            // have been returned as such (This simply means that the response
            // we got couldn't be serialised into the type we were expecting)
            Some(Document::Unexpected(change)) => {
                println!("{}",change)
            }
            // This is impossible in this particular example since there
            // needs to be at least one server available to give this
            // response otherwise we would have run into an error for
            // failing to connect
            None => {
                println!("got no documents in the database");
            }
        }
        Ok(())
    })
    // Our query ran into an error
        .or_else(|error| {
            println!("{:?}", error);
            Err(())
        });

    // Wait for all the results to be processed
    for _ in stati.wait() {}
}
<commit_msg>Added filter, fixed a bug with null values<commit_after>extern crate futures;
extern crate tokio_core;

#[macro_use]
extern crate reql;
extern crate reql_types;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

use futures::stream::Stream;
use tokio_core::reactor::Core;
use reql::{Client, Document, Run};

/**
 * rethinkdb changelog example
 * After running, access your admin panel at localhost:8080
 *
 * setup a "test" database, with a "test" table, and run:
 *
 * // Insert an item
 * r.db('test').table('test').insert({ test: 1 });
 *
 * // Give the first item a random number
 * r.db('test').table('test').nth(0).update({ test: r.random(0, 100) }, { nonAtomic: true });
 *
 * // Remove the first item
 * r.db('test').table('test').nth(0).delete();
 *
 */

#[derive(Debug, Serialize, Deserialize)]
struct TestItem {
    test: i32,
    id: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Change {
    #[serde(rename(deserialize = "type"))]
    action: String,
    // new_val or old_val can be null, so we should use Options
    new_val: Option<TestItem>,
    old_val: Option<TestItem>,
}

fn main()
{
    // Create a new ReQL client
    let r = Client::new();

    // Create an even loop
    let core = Core::new().unwrap();

    // Create a connection pool
    let conn = r.connect(&core.handle()).unwrap();

    // Run the query
    let query =
        r.db("test")
        .table("test")
        .filter(args!(|doc| {

            // Filter only documents which match our current TestItem trait
            doc.has_fields("test").and(doc.get_field("test").type_of().eq("NUMBER"))
        }))
        .changes()

        // We want rethinkdb to inform us of the change type
        .with_args(args!({
            include_types: true
        }))
        .run::<Change>(conn)
        .unwrap();

    // Process the results
    let stati = query.and_then(|change| {
        match change {
            // The server returned the response we were expecting,
            // and deserialized the data into our Change structure
            Some(Document::Expected(change)) => {
                // Extract the change type
                print!("{} action received\n\t=> ", change.action);

                // Match the change type
                match change.action.as_ref() {
                    "add" => println!("{:?}", change.new_val),
                    "remove" => println!("{:?}", change.old_val),
                    "change" => println!("from {:?} to {:?}", change.old_val, change.new_val),

                    _ => println!("{:?}", change)
                }
            }
            // We got a response alright, but it wasn't the one we were
            // expecting plus it's not an error either, otherwise it would
            // have been returned as such (This simply means that the response
            // we got couldn't be serialised into the type we were expecting)
            Some(Document::Unexpected(change)) => {
                println!("Got unexpected change: {}",change)
            }
            // This is impossible in this particular example since there
            // needs to be at least one server available to give this
            // response otherwise we would have run into an error for
            // failing to connect
            None => {
                println!("got no documents in the database");
            }
        }
        Ok(())
    })
    // Our query ran into an error
        .or_else(|error| {
            println!("{:?}", error);
            Err(())
        });

    // Wait for all the results to be processed
    for _ in stati.wait() {}
}
<|endoftext|>


<commit_before>extern crate neli;
use neli::consts::{CtrlAttr, CtrlCmd, GenlId, NlFamily, NlmF, Nlmsg};
use neli::err::NlError;
use neli::genl::Genlmsghdr;
use neli::nl::Nlmsghdr;
use neli::nlattr::Nlattr;
use neli::socket::NlSocket;
use neli::Nl;
use neli::StreamReadBuffer;

const GENL_VERSION: u8 = 2;

// This example attempts to mimic the "genl ctrl list" command. For simplicity, it only outputs
// the name and identifier of each generic netlink family.

fn main() -> Result<(), NlError> {
    let mut socket = NlSocket::connect(NlFamily::Generic, None, None, true)?;

    let attrs: Vec<Nlattr<CtrlAttr>> = vec![];
    let genlhdr = Genlmsghdr::new(CtrlCmd::Getfamily, GENL_VERSION, attrs)?;
    let nlhdr = {
        let len = None;
        let nl_type = GenlId::Ctrl;
        let flags = vec![NlmF::Request, NlmF::Dump];
        let seq = None;
        let pid = None;
        let payload = genlhdr;
        Nlmsghdr::new(len, nl_type, flags, seq, pid, payload)
    };
    socket.send_nl(nlhdr)?;

    loop {
        let response = socket.recv_nl::<GenlId, Genlmsghdr<CtrlCmd>>(None)?;

        if let GenlId::UnrecognizedVariant(id) = response.nl_type {
            match Nlmsg::from(id) {
                // This example could be improved by reinterpreting the payload as an Nlmsgerr
                // struct and printing the specific error encountered.
                Nlmsg::Error => panic!("An error occurred while retrieving available families."),
                Nlmsg::Done => break,
                _ => {}
            }
        }

        let mut handle = response.nl_payload.get_attr_handle::<CtrlAttr>();
        handle.parse_nested_attributes()?;

        for attr in handle.iter().unwrap() {
            match &attr.nla_type {
                CtrlAttr::FamilyName => {
                    let mut mem = StreamReadBuffer::new(&attr.payload);
                    mem.set_size_hint(attr.payload.len() - 1);
                    let name = String::deserialize(&mut mem)?;
                    println!("{}", name);
                }
                CtrlAttr::FamilyId => {
                    let mut mem = StreamReadBuffer::new(&attr.payload);
                    let id = u16::deserialize(&mut mem)?;
                    println!("\tID: 0x{:x}", id);
                }
                _ => {}
            }
        }
    }

    Ok(())
}
<commit_msg>Use Nlmsg type in ctrl-list example<commit_after>extern crate neli;
use neli::consts::{CtrlAttr, CtrlCmd, GenlId, NlFamily, NlmF, Nlmsg};
use neli::err::NlError;
use neli::genl::Genlmsghdr;
use neli::nl::Nlmsghdr;
use neli::nlattr::Nlattr;
use neli::socket::NlSocket;
use neli::Nl;
use neli::StreamReadBuffer;

const GENL_VERSION: u8 = 2;

// This example attempts to mimic the "genl ctrl list" command. For simplicity, it only outputs
// the name and identifier of each generic netlink family.

fn main() -> Result<(), NlError> {
    let mut socket = NlSocket::connect(NlFamily::Generic, None, None, true)?;

    let attrs: Vec<Nlattr<CtrlAttr>> = vec![];
    let genlhdr = Genlmsghdr::new(CtrlCmd::Getfamily, GENL_VERSION, attrs)?;
    let nlhdr = {
        let len = None;
        let nl_type = GenlId::Ctrl;
        let flags = vec![NlmF::Request, NlmF::Dump];
        let seq = None;
        let pid = None;
        let payload = genlhdr;
        Nlmsghdr::new(len, nl_type, flags, seq, pid, payload)
    };
    socket.send_nl(nlhdr)?;

    loop {
        let response = socket.recv_nl::<Nlmsg, Genlmsghdr<CtrlCmd>>(None)?;
        match response.nl_type {
            // This example could be improved by reinterpreting the payload as an Nlmsgerr struct
            // and printing the specific error encountered.
            Nlmsg::Error => panic!("An error occurred while retrieving available families."),
            Nlmsg::Done => break,
            _ => (),
        };

        let mut handle = response.nl_payload.get_attr_handle::<CtrlAttr>();
        handle.parse_nested_attributes()?;

        for attr in handle.iter().unwrap() {
            match &attr.nla_type {
                CtrlAttr::FamilyName => {
                    let mut mem = StreamReadBuffer::new(&attr.payload);
                    mem.set_size_hint(attr.payload.len() - 1);
                    let name = String::deserialize(&mut mem)?;
                    println!("{}", name);
                }
                CtrlAttr::FamilyId => {
                    let mut mem = StreamReadBuffer::new(&attr.payload);
                    let id = u16::deserialize(&mut mem)?;
                    println!("\tID: 0x{:x}", id);
                }
                _ => {}
            }
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>extern crate serialize;

use std::collections::HashMap;
use serialize::{Decodable, json};

static json: &'static str = include_str!("../data/mime.json");

pub struct Types {
    ext_by_type: HashMap<String, Vec<String>>,
    type_by_ext: HashMap<String, String>

}

impl Types {
    pub fn new() -> Result<Types, ()> {
        let parsed = try!(json::from_str(json).map_err(|_| ()));
        let mut decoder = json::Decoder::new(parsed);
        let decoded: HashMap<String, Vec<String>> =
            try!(Decodable::decode(&mut decoder).map_err(|_| ()));

        let mut by_ext = HashMap::new();

        for (mime_type, exts) in decoded.iter() {
            for ext in exts.iter() {
                by_ext.insert(ext.clone(), mime_type.clone());
            }
        }

        Ok(Types { ext_by_type: decoded, type_by_ext: by_ext })
    }

    pub fn get_extension<'a>(&'a self, name: &str) -> Option<&'a [String]> {
        self.ext_by_type.find_equiv(name).map(|v| v.as_slice())
    }

    pub fn get_mime_type<'a>(&'a self, ext: &str) -> Option<&'a str> {
        self.type_by_ext.find_equiv(ext).map(|v| v.as_slice())
    }

    pub fn mime_for_path<'a>(&'a self, path: &Path) -> &'a str {
        path.extension_str()
            .and_then(|ext| self.get_mime_type(ext))
            .unwrap_or_else(|| "text/plain")
    }
}

#[cfg(test)]
mod test {
    extern crate test;
    use Types;

    #[bench]
    fn bench_load_types(b: &mut test::Bencher) {
        b.iter(|| {
            Types::new()
        });
    }

    #[test]
    fn test_by_ext() {
        let t = Types::new().ok().expect("Types didn't load");
        assert_eq!(t.get_extension("text/css"), Some(["css".to_string()].as_slice()));
    }

    #[test]
    fn test_by_type() {
        let t = Types::new().ok().expect("Types didn't load");
        assert_eq!(t.get_mime_type("css"), Some("text/css"));
    }

    #[test]
    fn test_by_path() {
        let t = Types::new().ok().expect("Types didn't load");

        test_path(&t, "foo", "text/plain");
        test_path(&t, "/path/to/foo", "text/plain");
        test_path(&t, "foo.css", "text/css");
        test_path(&t, "/path/to/foo.css", "text/css");
        test_path(&t, "foo.html.css", "text/css");
        test_path(&t, "/path/to/foo.html.css", "text/css");
        test_path(&t, "/path/to.html/foo.css", "text/css");
    }

    fn test_path(types: &Types, path: &str, expected: &str) {
        assert_eq!(types.mime_for_path(&Path::new(path)), expected);
    }
}
<commit_msg>(feat) Derive some traits for Types<commit_after>extern crate serialize;

use std::collections::HashMap;
use serialize::{Decodable, json};

static json: &'static str = include_str!("../data/mime.json");

#[deriving(Show, Clone, Eq, PartialEq)]
pub struct Types {
    ext_by_type: HashMap<String, Vec<String>>,
    type_by_ext: HashMap<String, String>

}

impl Types {
    pub fn new() -> Result<Types, ()> {
        let parsed = try!(json::from_str(json).map_err(|_| ()));
        let mut decoder = json::Decoder::new(parsed);
        let decoded: HashMap<String, Vec<String>> =
            try!(Decodable::decode(&mut decoder).map_err(|_| ()));

        let mut by_ext = HashMap::new();

        for (mime_type, exts) in decoded.iter() {
            for ext in exts.iter() {
                by_ext.insert(ext.clone(), mime_type.clone());
            }
        }

        Ok(Types { ext_by_type: decoded, type_by_ext: by_ext })
    }

    pub fn get_extension<'a>(&'a self, name: &str) -> Option<&'a [String]> {
        self.ext_by_type.find_equiv(name).map(|v| v.as_slice())
    }

    pub fn get_mime_type<'a>(&'a self, ext: &str) -> Option<&'a str> {
        self.type_by_ext.find_equiv(ext).map(|v| v.as_slice())
    }

    pub fn mime_for_path<'a>(&'a self, path: &Path) -> &'a str {
        path.extension_str()
            .and_then(|ext| self.get_mime_type(ext))
            .unwrap_or_else(|| "text/plain")
    }
}

#[cfg(test)]
mod test {
    extern crate test;
    use Types;

    #[bench]
    fn bench_load_types(b: &mut test::Bencher) {
        b.iter(|| {
            Types::new()
        });
    }

    #[test]
    fn test_by_ext() {
        let t = Types::new().ok().expect("Types didn't load");
        assert_eq!(t.get_extension("text/css"), Some(["css".to_string()].as_slice()));
    }

    #[test]
    fn test_by_type() {
        let t = Types::new().ok().expect("Types didn't load");
        assert_eq!(t.get_mime_type("css"), Some("text/css"));
    }

    #[test]
    fn test_by_path() {
        let t = Types::new().ok().expect("Types didn't load");

        test_path(&t, "foo", "text/plain");
        test_path(&t, "/path/to/foo", "text/plain");
        test_path(&t, "foo.css", "text/css");
        test_path(&t, "/path/to/foo.css", "text/css");
        test_path(&t, "foo.html.css", "text/css");
        test_path(&t, "/path/to/foo.html.css", "text/css");
        test_path(&t, "/path/to.html/foo.css", "text/css");
    }

    fn test_path(types: &Types, path: &str, expected: &str) {
        assert_eq!(types.mime_for_path(&Path::new(path)), expected);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Implementations for sending logs to external log processes e.g. Logstash
//!
//! Handles sending logs under disconnects, and retries.  Tries to continue to make progress on a
//! log but eventually drops older logs to continue to make progress on newer logs.

use crate::counters::{STRUCT_LOG_CONNECT_ERROR_COUNT, STRUCT_LOG_TCP_CONNECT_COUNT};
use std::{
    io,
    io::Write,
    net::{TcpStream, ToSocketAddrs},
    time::Duration,
};

const WRITE_TIMEOUT_MS: u64 = 2000;
const CONNECTION_TIMEOUT_MS: u64 = 5000;

/// A wrapper for `TcpStream` that handles reconnecting to the endpoint automatically
///
/// `TcpWriter::write()` will block on the message until it is connected.
pub(crate) struct TcpWriter {
    /// The DNS name or IP address logs are being sent to
    endpoint: String,
    /// The `TCPStream` to write to, which will be `None` when disconnected
    stream: Option<TcpStream>,
}

impl TcpWriter {
    pub fn new(endpoint: String) -> Self {
        Self {
            endpoint,
            stream: None,
        }
    }

    pub fn endpoint(&self) -> &str {
        &self.endpoint
    }

    /// Ensure that we get a connection, no matter how long it takes
    /// This will block until there is a connection
    fn refresh_connection(&mut self) {
        loop {
            match self.connect() {
                Ok(stream) => {
                    self.stream = Some(stream);
                    return;
                }
                Err(e) => {
                    eprintln!("[Logging] Failed to connect: {}", e);
                    STRUCT_LOG_CONNECT_ERROR_COUNT.inc();
                }
            }

            // Sleep a second so this doesn't just spin as fast as possible
            std::thread::sleep(Duration::from_millis(1000));
        }
    }

    /// Connect and ensure the write timeout is set
    fn connect(&mut self) -> io::Result<TcpStream> {
        STRUCT_LOG_TCP_CONNECT_COUNT.inc();

        let mut last_error = io::Error::new(
            io::ErrorKind::Other,
            format!("Unable to resolve and connect to {}", self.endpoint),
        );

        // resolve addresses to handle DNS names
        for addr in self.endpoint.to_socket_addrs()? {
            match TcpStream::connect_timeout(&addr, Duration::from_millis(CONNECTION_TIMEOUT_MS)) {
                Ok(stream) => {
                    // Set the write timeout
                    if let Err(err) =
                        stream.set_write_timeout(Some(Duration::from_millis(WRITE_TIMEOUT_MS)))
                    {
                        STRUCT_LOG_CONNECT_ERROR_COUNT.inc();
                        eprintln!("[Logging] Failed to set write timeout: {}", err);
                        continue;
                    }
                    return Ok(stream);
                }
                Err(err) => last_error = err,
            }
        }

        Err(last_error)
    }
}

impl Write for TcpWriter {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        // Refresh the connection if it's missing
        if self.stream.is_none() {
            self.refresh_connection();
        }

        // Attempt to write, and if it fails clear underlying stream
        // This doesn't guarantee a message cut off mid send will work, but it does guarantee that
        // we will connect first
        self.stream
            .as_mut()
            .ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "No stream"))
            .and_then(|stream| stream.write(buf))
            .map_err(|e| {
                self.stream = None;
                e
            })
    }

    fn flush(&mut self) -> io::Result<()> {
        if let Some(mut stream) = self.stream.as_ref() {
            stream.flush()
        } else {
            Err(io::Error::new(
                io::ErrorKind::NotConnected,
                "Can't flush, not connected",
            ))
        }
    }
}
<commit_msg>logger: only attempt to reconnect once a second<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Implementations for sending logs to external log processes e.g. Logstash
//!
//! Handles sending logs under disconnects, and retries.  Tries to continue to make progress on a
//! log but eventually drops older logs to continue to make progress on newer logs.

use crate::counters::{STRUCT_LOG_CONNECT_ERROR_COUNT, STRUCT_LOG_TCP_CONNECT_COUNT};
use std::{
    io,
    io::Write,
    net::{TcpStream, ToSocketAddrs},
    time::{Duration, Instant},
};

const WRITE_TIMEOUT_MS: u64 = 2000;
const CONNECTION_TIMEOUT_MS: u64 = 5000;

/// A wrapper for `TcpStream` that handles reconnecting to the endpoint automatically
///
/// `TcpWriter::write()` will block on the message until it is connected.
pub(crate) struct TcpWriter {
    /// The DNS name or IP address logs are being sent to
    endpoint: String,
    /// The `TCPStream` to write to, which will be `None` when disconnected
    stream: Option<TcpStream>,
    last_connection_attempt: Option<Instant>,
}

impl TcpWriter {
    pub fn new(endpoint: String) -> Self {
        Self {
            endpoint,
            stream: None,
            last_connection_attempt: None,
        }
    }

    pub fn endpoint(&self) -> &str {
        &self.endpoint
    }

    /// Ensure that we get a connection, no matter how long it takes
    /// This will block until there is a connection
    fn refresh_connection(&mut self) -> io::Result<()> {
        // Only refresh the connection once a second
        if self
            .last_connection_attempt
            .map(|t| t.elapsed() > Duration::from_millis(1000))
            .unwrap_or(true)
        {
            self.last_connection_attempt = Some(Instant::now());
            match self.connect() {
                Ok(stream) => {
                    self.stream = Some(stream);
                    Ok(())
                }
                Err(e) => {
                    eprintln!("[Logging] Failed to connect: {}", e);
                    STRUCT_LOG_CONNECT_ERROR_COUNT.inc();
                    Err(e)
                }
            }
        } else {
            Err(io::Error::new(
                io::ErrorKind::Other,
                "unable to refresh connection",
            ))
        }
    }

    /// Connect and ensure the write timeout is set
    fn connect(&mut self) -> io::Result<TcpStream> {
        STRUCT_LOG_TCP_CONNECT_COUNT.inc();

        let mut last_error = io::Error::new(
            io::ErrorKind::Other,
            format!("Unable to resolve and connect to {}", self.endpoint),
        );

        // resolve addresses to handle DNS names
        for addr in self.endpoint.to_socket_addrs()? {
            match TcpStream::connect_timeout(&addr, Duration::from_millis(CONNECTION_TIMEOUT_MS)) {
                Ok(stream) => {
                    // Set the write timeout
                    if let Err(err) =
                        stream.set_write_timeout(Some(Duration::from_millis(WRITE_TIMEOUT_MS)))
                    {
                        STRUCT_LOG_CONNECT_ERROR_COUNT.inc();
                        eprintln!("[Logging] Failed to set write timeout: {}", err);
                        continue;
                    }
                    return Ok(stream);
                }
                Err(err) => last_error = err,
            }
        }

        Err(last_error)
    }
}

impl Write for TcpWriter {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        // Refresh the connection if it's missing
        if self.stream.is_none() {
            self.refresh_connection()?;
        }

        // Attempt to write, and if it fails clear underlying stream
        // This doesn't guarantee a message cut off mid send will work, but it does guarantee that
        // we will connect first
        self.stream
            .as_mut()
            .ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "No stream"))
            .and_then(|stream| stream.write(buf))
            .map_err(|e| {
                self.stream = None;
                e
            })
    }

    fn flush(&mut self) -> io::Result<()> {
        if let Some(mut stream) = self.stream.as_ref() {
            stream.flush()
        } else {
            Err(io::Error::new(
                io::ErrorKind::NotConnected,
                "Can't flush, not connected",
            ))
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate libeuler;

use std::collections::HashSet;
/// All square roots are periodic when written as continued fractions and can be written in the form:
/// √N = a0 +          1
///           -----------------
///           a1 +       1
///               -------------
///               a2 +     1
///                    --------
///                    a3 + ...
///
/// For example, let us consider √23:
/// √23 = 4 + √23 — 4 = 4 +   1   = 4 +       1
///                         -----       -----------
///                           1         1 + √23 – 3
///                         -----           -------
///                         √23—4              7
///
/// If we continue we would get the following expansion:
/// √23 = 4 +           1
///           ------------------
///           1 +         1
///               --------------
///               3 +      1
///                   ----------
///                   1 +   1
///                      -------
///                      8 + ...
///
/// It can be seen that the sequence is repeating. For conciseness, we use the notation √23 =
/// [4;(1,3,1,8)], to indicate that the block (1,3,1,8) repeats indefinitely.
///
/// The first ten continued fraction representations of (irrational) square roots are:
///
/// √2=[1;(2)], period=1
/// √3=[1;(1,2)], period=2
/// √5=[2;(4)], period=1
/// √6=[2;(2,4)], period=2
/// √7=[2;(1,1,1,4)], period=4
/// √8=[2;(1,4)], period=2
/// √10=[3;(6)], period=1
/// √11=[3;(3,6)], period=2
/// √12= [3;(2,6)], period=2
/// √13=[3;(1,1,1,1,6)], period=5
///
/// Exactly four continued fractions, for N ≤ 13, have an odd period.
///
/// How many continued fractions for N ≤ 10000 have an odd period?
fn main() {
    solutions! {
        inputs: (max_n: i64 = 10_000)

        sol naive {
            let squares: Vec<(i64, i64)> = (0..).zip(
                    (0..)
                        .map(|n| n*n)
                        .take_while(|&n| n <= max_n)
                ).collect();

            let mut count = 0;

            for subject in 1..(max_n + 1) {
                let &(base, value) = squares.iter()
                    .filter(|&&(_, pow)| pow <= subject)
                    .last()
                    .unwrap();

                if value == subject {
                    continue;
                }

                let mut top = 1;
                let mut bot_number = -base;

                let mut seen_states = HashSet::new();
                let mut period = 0;

                for _ in 0.. {
                    let key = (bot_number, top);

                    if seen_states.contains(&key) {
                        break;
                    }
                    seen_states.insert(key);

                    top = (subject - bot_number*bot_number) / top;

                    let mut to_subtract = 0;

                    while -bot_number - to_subtract - top >= -base {
                        to_subtract += top;
                    }

                    bot_number = -bot_number - to_subtract;

                    period += 1;
                }

                if period % 2 != 0 {
                    count += 1;
                }
            }

            count
        }
    }
}
<commit_msg>Add stolen solution to problem 64<commit_after>#[macro_use] extern crate libeuler;

use std::collections::HashSet;
/// All square roots are periodic when written as continued fractions and can be written in the form:
/// √N = a0 +          1
///           -----------------
///           a1 +       1
///               -------------
///               a2 +     1
///                    --------
///                    a3 + ...
///
/// For example, let us consider √23:
/// √23 = 4 + √23 — 4 = 4 +   1   = 4 +       1
///                         -----       -----------
///                           1         1 + √23 – 3
///                         -----           -------
///                         √23—4              7
///
/// If we continue we would get the following expansion:
/// √23 = 4 +           1
///           ------------------
///           1 +         1
///               --------------
///               3 +      1
///                   ----------
///                   1 +   1
///                      -------
///                      8 + ...
///
/// It can be seen that the sequence is repeating. For conciseness, we use the notation √23 =
/// [4;(1,3,1,8)], to indicate that the block (1,3,1,8) repeats indefinitely.
///
/// The first ten continued fraction representations of (irrational) square roots are:
///
/// √2=[1;(2)], period=1
/// √3=[1;(1,2)], period=2
/// √5=[2;(4)], period=1
/// √6=[2;(2,4)], period=2
/// √7=[2;(1,1,1,4)], period=4
/// √8=[2;(1,4)], period=2
/// √10=[3;(6)], period=1
/// √11=[3;(3,6)], period=2
/// √12= [3;(2,6)], period=2
/// √13=[3;(1,1,1,1,6)], period=5
///
/// Exactly four continued fractions, for N ≤ 13, have an odd period.
///
/// How many continued fractions for N ≤ 10000 have an odd period?
fn main() {
    solutions! {
        inputs: (max_n: i64 = 10_000)

        sol naive {
            let squares: Vec<(i64, i64)> = (0..).zip(
                    (0..)
                        .map(|n| n*n)
                        .take_while(|&n| n <= max_n)
                ).collect();

            let mut count = 0;

            for subject in 1..(max_n + 1) {
                let &(base, value) = squares.iter()
                    .filter(|&&(_, pow)| pow <= subject)
                    .last()
                    .unwrap();

                if value == subject {
                    continue;
                }

                let mut top = 1;
                let mut bot_number = -base;

                let mut seen_states = HashSet::new();
                let mut period = 0;

                for _ in 0.. {
                    let key = (bot_number, top);

                    if seen_states.contains(&key) {
                        break;
                    }
                    seen_states.insert(key);

                    top = (subject - bot_number*bot_number) / top;

                    let mut to_subtract = 0;

                    while -bot_number - to_subtract - top >= -base {
                        to_subtract += top;
                    }

                    bot_number = -bot_number - to_subtract;

                    period += 1;
                }

                if period % 2 != 0 {
                    count += 1;
                }
            }

            count
        }

        sol cheap {
            let mut result = 0;

            for n in 2..(max_n + 1) {
                let limit = (n as f64).sqrt() as i64;
                if limit*limit == n { continue; }

                let mut period = 0;
                let mut d = 1;
                let mut m = 0;
                let mut a = limit;

                loop {
                    m = d*a - m;
                    d = (n - m * m) / d;
                    a = (limit + m) / d;
                    period += 1;
                    if a == 2*limit { break; }
                }

                if period % 2 == 1 { result += 1; }
            }

            result
        }
    }
}
<|endoftext|>