<commit_before>use std::collections::HashMap;
use std::sync::atomic::Ordering;

use ctxt::SemContext;
use gc::Address;
use gc::root::IndirectObj;
use gc::space::Space;
use gc::swiper::card::CardTable;
use gc::swiper::CARD_SIZE;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::{in_kilo, on_different_cards};
use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::Region;
use gc::swiper::young::YoungGen;
use mem;
use object::{offset_of_array_data, Obj};
use os;
use timer::{in_ms, Timer};

pub struct FullCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    heap: Region,
    young: &'a YoungGen,
    old: &'a OldGen,
    large_space: &'a LargeSpace,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    perm_space: &'a Space,

    marking_bitmap: MarkingBitmap,
    fwd_table: ForwardTable,

    fwd: Address,
    fwd_end: Address,
    old_top: Address,
}

impl<'a, 'ast> FullCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        heap: Region,
        young: &'a YoungGen,
        old: &'a OldGen,
        large_space: &'a LargeSpace,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        perm_space: &'a Space,
        rootset: &'a [IndirectObj],
    ) -> FullCollector<'a, 'ast> {
        let marking_bitmap = MarkingBitmap::new(heap.clone());

        FullCollector {
            ctxt: ctxt,
            heap: heap,
            young: young,
            old: old,
            large_space: large_space,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            perm_space: perm_space,

            marking_bitmap: marking_bitmap,
            fwd_table: ForwardTable::new(),

            fwd: old.total.start,
            fwd_end: old.total.end,
            old_top: Address::null(),
        }
    }

    pub fn collect(&mut self) {
        let active = self.ctxt.args.flag_gc_verbose;
        let mut timer = Timer::new(active);
        let init_size = self.heap_size();

        let time_mark = Timer::ms(active, || {
            self.mark_live();
        });

        let time_forward = Timer::ms(active, || {
            self.compute_forward();
        });

        let time_updateref = Timer::ms(active, || {
            self.update_references();
        });

        let time_relocate = Timer::ms(active, || {
            self.relocate();
        });

        let time_large = Timer::ms(active, || {
            self.update_large_objects();
        });

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let garbage = init_size - new_size;
            let garbage_ratio = (garbage as f64 / init_size as f64) * 100f64;

            println!(
                "GC: Full GC ({:.2} ms, {:.1}K->{:.1}K size, {:.1}K/{:.0}% garbage); mark={:.1}ms forward={:.1}ms updateref={:.1}ms relocate={:.1}ms large={:.1}ms",
                in_ms(dur),
                in_kilo(init_size),
                in_kilo(new_size),
                in_kilo(garbage),
                garbage_ratio,
                time_mark,
                time_forward,
                time_updateref,
                time_relocate,
                time_large,
            );
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn mark_live(&mut self) {
        let mut marking_stack: Vec<Address> = Vec::new();
        let mut live_objects = 0;

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if self.heap.contains(root_ptr) {
                if !self.is_marked_addr(root_ptr) {
                    marking_stack.push(root_ptr);
                    self.mark(root_ptr);
                    live_objects += 1;
                }
            } else {
                debug_assert!(root_ptr.is_null() || self.perm_space.contains(root_ptr));
            }
        }

        while marking_stack.len() > 0 {
            let object_addr = marking_stack.pop().expect("stack already empty");
            let object = unsafe { &mut *object_addr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_addr = Address::from_ptr(field.get());

                if self.heap.contains(field_addr) {
                    if !self.is_marked_addr(field_addr) {
                        marking_stack.push(field_addr);
                        self.mark(field_addr);
                        live_objects += 1;
                    }
                } else {
                    debug_assert!(field_addr.is_null() || self.perm_space.contains(field_addr));
                }
            });
        }

        self.fwd_table.reserve_capacity(live_objects);
    }

    fn compute_forward(&mut self) {
        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let fwd = full.allocate(object_size);
                full.fwd_table.forward_to(address, fwd);
            }
        });
    }

    fn update_references(&mut self) {
        self.walk_old_and_young(|full, object, _address, _| {
            if full.is_marked(object) {
                object.visit_reference_fields(|field| {
                    full.forward_reference(field);
                });
            }
        });

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if !root_ptr.is_null() && !self.perm_space.contains(root_ptr)
                && !self.large_space.contains(root_ptr)
            {
                let fwd_addr = self.fwd_table.forward_address(root_ptr);
                root.set(fwd_addr.to_mut_ptr());
            }
        }
    }

    fn relocate(&mut self) {
        self.young.unprotect_to_space();

        self.crossing_map.set_first_object(0.into(), 0);
        let mut last_dest = Address::null();

        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let dest = full.fwd_table.forward_address(address);
                object.copy_to(dest, object_size);

                let next_dest = dest.offset(object_size);

                if on_different_cards(dest, next_dest) && full.old.contains(dest) {
                    full.update_crossing(dest, next_dest, object.is_array_ref());
                }

                last_dest = next_dest;
            }
        });

        let young_top;
        let old_top;

        // check if we have left the old-generation into
        // the young generation for copying objects
        if self.old_top.is_null() {
            // if not, young gen is empty
            young_top = self.young.to_space().start;
            old_top = self.fwd;
        } else {
            young_top = self.fwd;
            old_top = self.old_top;
        }

        debug_assert!(self.young.valid_top(young_top));
        self.young.swap_spaces(young_top);
        self.young.protect_to_space();

        debug_assert!(self.old.valid_top(old_top));
        self.old.free.store(old_top.to_usize(), Ordering::SeqCst);
    }

    fn update_large_objects(&mut self) {
        self.large_space.remove_objects(|addr| {
            if !self.is_marked_addr(addr) {
                // free object
                return false;
            }

            let object = unsafe { &mut *addr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                self.forward_reference(field);
            });

            // keep object
            true
        });
    }

    fn forward_reference(&mut self, indirect_obj: IndirectObj) {
        let object_addr = Address::from_ptr(indirect_obj.get());

        if !object_addr.is_null() && !self.perm_space.contains(object_addr)
            && !self.large_space.contains(object_addr)
        {
            let fwd_addr = self.fwd_table.forward_address(object_addr);
            indirect_obj.set(fwd_addr.to_mut_ptr());
        }
    }

    fn walk_old_and_young<F>(&mut self, mut fct: F)
    where
        F: FnMut(&mut FullCollector, &mut Obj, Address, usize),
    {
        let used_region = self.old.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);

        let used_region = self.young.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);
    }

    fn walk_region<F>(&mut self, start: Address, end: Address, fct: &mut F)
    where
        F: FnMut(&mut FullCollector, &mut Obj, Address, usize),
    {
        let mut scan = start;

        while scan < end {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };
            let object_size = object.size();

            fct(self, object, scan, object_size);

            scan = scan.offset(object_size);
        }
    }

    fn allocate(&mut self, object_size: usize) -> Address {
        let addr = self.fwd;
        let next = self.fwd.offset(object_size);

        if next <= self.fwd_end {
            self.fwd = next;
            return addr;
        }

        panic!("FAIL: Not enough space for objects in old generation.");
    }

    fn update_crossing(&mut self, last: Address, addr: Address, array_ref: bool) {
        let last_card = self.old.card_from_address(last);

        let offset = addr.to_usize() & (CARD_SIZE - 1);
        let offset_words = offset / mem::ptr_width_usize();

        let card = self.old.card_from_address(addr);

        if array_ref {
            let last_card_end = self.old.address_from_card(last_card).offset(CARD_SIZE);
            let loop_start;

            if last.offset(offset_of_array_data() as usize) > last_card_end {
                let diff_words = last_card_end.offset_from(last) / mem::ptr_width_usize();
                self.crossing_map
                    .set_array_start((last_card.to_usize() + 1).into(), diff_words);

                loop_start = last_card.to_usize() + 2;
            } else {
                loop_start = last_card.to_usize() + 1;
            }

            let refs_per_card = CARD_SIZE / mem::ptr_width_usize();

            for i in loop_start..card.to_usize() {
                self.crossing_map
                    .set_references_at_start(i.into(), refs_per_card);
            }

            if card.to_usize() >= loop_start {
                self.crossing_map
                    .set_references_at_start(card, offset_words);
            }
        } else {
            for i in last_card.to_usize() + 1..card.to_usize() {
                self.crossing_map.set_no_references(i.into());
            }

            self.crossing_map.set_first_object(card, offset_words);
        }
    }

    fn is_marked(&self, obj: &Obj) -> bool {
        self.marking_bitmap
            .is_marked(Address::from_ptr(obj as *const _))
    }

    fn is_marked_addr(&self, addr: Address) -> bool {
        self.marking_bitmap.is_marked(addr)
    }

    fn mark(&mut self, addr: Address) {
        self.marking_bitmap.mark(addr);
    }
}

pub struct MarkingBitmap {
    heap: Region,
    bitmap: Region,
}

impl MarkingBitmap {
    pub fn new(heap: Region) -> MarkingBitmap {
        let bitmap_size = mem::page_align(heap.size() / mem::ptr_width_usize() / 8);
        let bitmap_start = Address::from_ptr(os::mmap(bitmap_size, os::Writable));

        if bitmap_start.is_null() {
            panic!("could not allocate marking bitmap.");
        }

        MarkingBitmap {
            heap: heap,
            bitmap: Region::new(bitmap_start, bitmap_start.offset(bitmap_size)),
        }
    }

    pub fn mark(&mut self, addr: Address) {
        let (byte_offset, bit_offset) = self.mark_offset(addr);

        let byte = self.mark_byte(byte_offset);
        let modified_byte = byte | (1 << bit_offset);
        self.set_mark_byte(byte_offset, modified_byte);
    }

    pub fn is_marked(&self, addr: Address) -> bool {
        let (byte_offset, bit_offset) = self.mark_offset(addr);
        let byte = self.mark_byte(byte_offset);

        byte & (1 << bit_offset) != 0
    }

    fn mark_offset(&self, addr: Address) -> (usize, u8) {
        debug_assert!(self.heap.contains(addr));
        let offset = addr.offset_from(self.heap.start) / mem::ptr_width_usize();
        let byte = offset / 8;
        let bit = offset % 8;

        (byte, bit as u8)
    }

    fn mark_byte(&self, offset: usize) -> u8 {
        let addr = self.bitmap.start.offset(offset);

        unsafe { *addr.to_ptr() }
    }

    fn set_mark_byte(&self, offset: usize, val: u8) {
        let addr = self.bitmap.start.offset(offset);

        unsafe {
            *addr.to_mut_ptr() = val;
        }
    }
}

impl Drop for MarkingBitmap {
    fn drop(&mut self) {
        os::munmap(self.bitmap.start.to_ptr(), self.bitmap.size());
    }
}

struct ForwardTable {
    data: HashMap<Address, Address>,
}

impl ForwardTable {
    fn new() -> ForwardTable {
        ForwardTable {
            data: HashMap::new(),
        }
    }

    fn reserve_capacity(&mut self, count: usize) {
        self.data.reserve(count);
    }

    fn forward_to(&mut self, addr: Address, fwd: Address) {
        self.data.insert(addr, fwd);
    }

    fn forward_address(&mut self, addr: Address) -> Address {
        *self.data.get(&addr).expect("no forward address found.")
    }
}
<commit_msg>swiper: split message<commit_after>use std::collections::HashMap;
use std::sync::atomic::Ordering;

use ctxt::SemContext;
use gc::Address;
use gc::root::IndirectObj;
use gc::space::Space;
use gc::swiper::card::CardTable;
use gc::swiper::CARD_SIZE;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::{in_kilo, on_different_cards};
use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::Region;
use gc::swiper::young::YoungGen;
use mem;
use object::{offset_of_array_data, Obj};
use os;
use timer::{in_ms, Timer};

pub struct FullCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    heap: Region,
    young: &'a YoungGen,
    old: &'a OldGen,
    large_space: &'a LargeSpace,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    perm_space: &'a Space,

    marking_bitmap: MarkingBitmap,
    fwd_table: ForwardTable,

    fwd: Address,
    fwd_end: Address,
    old_top: Address,
}

impl<'a, 'ast> FullCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        heap: Region,
        young: &'a YoungGen,
        old: &'a OldGen,
        large_space: &'a LargeSpace,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        perm_space: &'a Space,
        rootset: &'a [IndirectObj],
    ) -> FullCollector<'a, 'ast> {
        let marking_bitmap = MarkingBitmap::new(heap.clone());

        FullCollector {
            ctxt: ctxt,
            heap: heap,
            young: young,
            old: old,
            large_space: large_space,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            perm_space: perm_space,

            marking_bitmap: marking_bitmap,
            fwd_table: ForwardTable::new(),

            fwd: old.total.start,
            fwd_end: old.total.end,
            old_top: Address::null(),
        }
    }

    pub fn collect(&mut self) {
        let active = self.ctxt.args.flag_gc_verbose;
        let mut timer = Timer::new(active);
        let init_size = self.heap_size();

        let time_mark = Timer::ms(active, || {
            self.mark_live();
        });

        let time_forward = Timer::ms(active, || {
            self.compute_forward();
        });

        let time_updateref = Timer::ms(active, || {
            self.update_references();
        });

        let time_relocate = Timer::ms(active, || {
            self.relocate();
        });

        let time_large = Timer::ms(active, || {
            self.update_large_objects();
        });

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let garbage = init_size - new_size;
            let garbage_ratio = (garbage as f64 / init_size as f64) * 100f64;

            println!(
                "GC: Full GC ({:.2} ms, {:.1}K->{:.1}K size, {:.1}K/{:.0}% garbage); \
                mark={:.1}ms forward={:.1}ms updateref={:.1}ms relocate={:.1}ms large={:.1}ms",
                in_ms(dur),
                in_kilo(init_size),
                in_kilo(new_size),
                in_kilo(garbage),
                garbage_ratio,
                time_mark,
                time_forward,
                time_updateref,
                time_relocate,
                time_large,
            );
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn mark_live(&mut self) {
        let mut marking_stack: Vec<Address> = Vec::new();
        let mut live_objects = 0;

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if self.heap.contains(root_ptr) {
                if !self.is_marked_addr(root_ptr) {
                    marking_stack.push(root_ptr);
                    self.mark(root_ptr);
                    live_objects += 1;
                }
            } else {
                debug_assert!(root_ptr.is_null() || self.perm_space.contains(root_ptr));
            }
        }

        while marking_stack.len() > 0 {
            let object_addr = marking_stack.pop().expect("stack already empty");
            let object = unsafe { &mut *object_addr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_addr = Address::from_ptr(field.get());

                if self.heap.contains(field_addr) {
                    if !self.is_marked_addr(field_addr) {
                        marking_stack.push(field_addr);
                        self.mark(field_addr);
                        live_objects += 1;
                    }
                } else {
                    debug_assert!(field_addr.is_null() || self.perm_space.contains(field_addr));
                }
            });
        }

        self.fwd_table.reserve_capacity(live_objects);
    }

    fn compute_forward(&mut self) {
        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let fwd = full.allocate(object_size);
                full.fwd_table.forward_to(address, fwd);
            }
        });
    }

    fn update_references(&mut self) {
        self.walk_old_and_young(|full, object, _address, _| {
            if full.is_marked(object) {
                object.visit_reference_fields(|field| {
                    full.forward_reference(field);
                });
            }
        });

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if !root_ptr.is_null() && !self.perm_space.contains(root_ptr)
                && !self.large_space.contains(root_ptr)
            {
                let fwd_addr = self.fwd_table.forward_address(root_ptr);
                root.set(fwd_addr.to_mut_ptr());
            }
        }
    }

    fn relocate(&mut self) {
        self.young.unprotect_to_space();

        self.crossing_map.set_first_object(0.into(), 0);
        let mut last_dest = Address::null();

        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let dest = full.fwd_table.forward_address(address);
                object.copy_to(dest, object_size);

                let next_dest = dest.offset(object_size);

                if on_different_cards(dest, next_dest) && full.old.contains(dest) {
                    full.update_crossing(dest, next_dest, object.is_array_ref());
                }

                last_dest = next_dest;
            }
        });

        let young_top;
        let old_top;

        // check if we have left the old-generation into
        // the young generation for copying objects
        if self.old_top.is_null() {
            // if not, young gen is empty
            young_top = self.young.to_space().start;
            old_top = self.fwd;
        } else {
            young_top = self.fwd;
            old_top = self.old_top;
        }

        debug_assert!(self.young.valid_top(young_top));
        self.young.swap_spaces(young_top);
        self.young.protect_to_space();

        debug_assert!(self.old.valid_top(old_top));
        self.old.free.store(old_top.to_usize(), Ordering::SeqCst);
    }

    fn update_large_objects(&mut self) {
        self.large_space.remove_objects(|addr| {
            if !self.is_marked_addr(addr) {
                // free object
                return false;
            }

            let object = unsafe { &mut *addr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                self.forward_reference(field);
            });

            // keep object
            true
        });
    }

    fn forward_reference(&mut self, indirect_obj: IndirectObj) {
        let object_addr = Address::from_ptr(indirect_obj.get());

        if !object_addr.is_null() && !self.perm_space.contains(object_addr)
            && !self.large_space.contains(object_addr)
        {
            let fwd_addr = self.fwd_table.forward_address(object_addr);
            indirect_obj.set(fwd_addr.to_mut_ptr());
        }
    }

    fn walk_old_and_young<F>(&mut self, mut fct: F)
    where
        F: FnMut(&mut FullCollector, &mut Obj, Address, usize),
    {
        let used_region = self.old.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);

        let used_region = self.young.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);
    }

    fn walk_region<F>(&mut self, start: Address, end: Address, fct: &mut F)
    where
        F: FnMut(&mut FullCollector, &mut Obj, Address, usize),
    {
        let mut scan = start;

        while scan < end {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };
            let object_size = object.size();

            fct(self, object, scan, object_size);

            scan = scan.offset(object_size);
        }
    }

    fn allocate(&mut self, object_size: usize) -> Address {
        let addr = self.fwd;
        let next = self.fwd.offset(object_size);

        if next <= self.fwd_end {
            self.fwd = next;
            return addr;
        }

        panic!("FAIL: Not enough space for objects in old generation.");
    }

    fn update_crossing(&mut self, last: Address, addr: Address, array_ref: bool) {
        let last_card = self.old.card_from_address(last);

        let offset = addr.to_usize() & (CARD_SIZE - 1);
        let offset_words = offset / mem::ptr_width_usize();

        let card = self.old.card_from_address(addr);

        if array_ref {
            let last_card_end = self.old.address_from_card(last_card).offset(CARD_SIZE);
            let loop_start;

            if last.offset(offset_of_array_data() as usize) > last_card_end {
                let diff_words = last_card_end.offset_from(last) / mem::ptr_width_usize();
                self.crossing_map
                    .set_array_start((last_card.to_usize() + 1).into(), diff_words);

                loop_start = last_card.to_usize() + 2;
            } else {
                loop_start = last_card.to_usize() + 1;
            }

            let refs_per_card = CARD_SIZE / mem::ptr_width_usize();

            for i in loop_start..card.to_usize() {
                self.crossing_map
                    .set_references_at_start(i.into(), refs_per_card);
            }

            if card.to_usize() >= loop_start {
                self.crossing_map
                    .set_references_at_start(card, offset_words);
            }
        } else {
            for i in last_card.to_usize() + 1..card.to_usize() {
                self.crossing_map.set_no_references(i.into());
            }

            self.crossing_map.set_first_object(card, offset_words);
        }
    }

    fn is_marked(&self, obj: &Obj) -> bool {
        self.marking_bitmap
            .is_marked(Address::from_ptr(obj as *const _))
    }

    fn is_marked_addr(&self, addr: Address) -> bool {
        self.marking_bitmap.is_marked(addr)
    }

    fn mark(&mut self, addr: Address) {
        self.marking_bitmap.mark(addr);
    }
}

pub struct MarkingBitmap {
    heap: Region,
    bitmap: Region,
}

impl MarkingBitmap {
    pub fn new(heap: Region) -> MarkingBitmap {
        let bitmap_size = mem::page_align(heap.size() / mem::ptr_width_usize() / 8);
        let bitmap_start = Address::from_ptr(os::mmap(bitmap_size, os::Writable));

        if bitmap_start.is_null() {
            panic!("could not allocate marking bitmap.");
        }

        MarkingBitmap {
            heap: heap,
            bitmap: Region::new(bitmap_start, bitmap_start.offset(bitmap_size)),
        }
    }

    pub fn mark(&mut self, addr: Address) {
        let (byte_offset, bit_offset) = self.mark_offset(addr);

        let byte = self.mark_byte(byte_offset);
        let modified_byte = byte | (1 << bit_offset);
        self.set_mark_byte(byte_offset, modified_byte);
    }

    pub fn is_marked(&self, addr: Address) -> bool {
        let (byte_offset, bit_offset) = self.mark_offset(addr);
        let byte = self.mark_byte(byte_offset);

        byte & (1 << bit_offset) != 0
    }

    fn mark_offset(&self, addr: Address) -> (usize, u8) {
        debug_assert!(self.heap.contains(addr));
        let offset = addr.offset_from(self.heap.start) / mem::ptr_width_usize();
        let byte = offset / 8;
        let bit = offset % 8;

        (byte, bit as u8)
    }

    fn mark_byte(&self, offset: usize) -> u8 {
        let addr = self.bitmap.start.offset(offset);

        unsafe { *addr.to_ptr() }
    }

    fn set_mark_byte(&self, offset: usize, val: u8) {
        let addr = self.bitmap.start.offset(offset);

        unsafe {
            *addr.to_mut_ptr() = val;
        }
    }
}

impl Drop for MarkingBitmap {
    fn drop(&mut self) {
        os::munmap(self.bitmap.start.to_ptr(), self.bitmap.size());
    }
}

struct ForwardTable {
    data: HashMap<Address, Address>,
}

impl ForwardTable {
    fn new() -> ForwardTable {
        ForwardTable {
            data: HashMap::new(),
        }
    }

    fn reserve_capacity(&mut self, count: usize) {
        self.data.reserve(count);
    }

    fn forward_to(&mut self, addr: Address, fwd: Address) {
        self.data.insert(addr, fwd);
    }

    fn forward_address(&mut self, addr: Address) -> Address {
        *self.data.get(&addr).expect("no forward address found.")
    }
}
<|endoftext|>


<commit_before>// Run clippy on a fixed set of crates and collect the warnings.
// This helps observing the impact clippy changs have on a set of real-world code.
//
// When a new lint is introduced, we can search the results for new warnings and check for false
// positives.

#![allow(clippy::filter_map)]

use crate::clippy_project_root;

use std::collections::HashMap;
use std::process::Command;
use std::{fmt, fs::write, path::PathBuf};

use serde::{Deserialize, Serialize};
use serde_json::Value;

// use this to store the crates when interacting with the crates.toml file
#[derive(Debug, Serialize, Deserialize)]
struct CrateList {
    crates: HashMap<String, Vec<String>>,
}

// crate data we stored in the toml, can have multiple versions per crate
// A single TomlCrate is laster mapped to several CrateSources in that case
struct TomlCrate {
    name: String,
    versions: Vec<String>,
}

// represents an archive we download from crates.io
#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
struct CrateSource {
    name: String,
    version: String,
}

// represents the extracted sourcecode of a crate
#[derive(Debug)]
struct Crate {
    version: String,
    name: String,
    // path to the extracted sources that clippy can check
    path: PathBuf,
}

#[derive(Debug)]
struct ClippyWarning {
    crate_name: String,
    crate_version: String,
    file: String,
    line: String,
    column: String,
    linttype: String,
    message: String,
}

impl std::fmt::Display for ClippyWarning {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(
            f,
            r#"{}/{}/{}:{}:{} {} "{}""#,
            &self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message
        )
    }
}

impl CrateSource {
    fn download_and_extract(&self) -> Crate {
        let extract_dir = PathBuf::from("target/crater/crates");
        let krate_download_dir = PathBuf::from("target/crater/downloads");

        // url to download the crate from crates.io
        let url = format!(
            "https://crates.io/api/v1/crates/{}/{}/download",
            self.name, self.version
        );
        println!("Downloading and extracting {} {} from {}", self.name, self.version, url);
        let _ = std::fs::create_dir("target/crater/");
        let _ = std::fs::create_dir(&krate_download_dir);
        let _ = std::fs::create_dir(&extract_dir);

        let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", &self.name, &self.version));
        // don't download/extract if we already have done so
        if !krate_file_path.is_file() {
            // create a file path to download and write the crate data into
            let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
            let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
            // copy the crate into the file
            std::io::copy(&mut krate_req, &mut krate_dest).unwrap();

            // unzip the tarball
            let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
            // extract the tar archive
            let mut archive = tar::Archive::new(ungz_tar);
            archive.unpack(&extract_dir).expect("Failed to extract!");
        }
        // crate is extracted, return a new Krate object which contains the path to the extracted
        // sources that clippy can check
        Crate {
            version: self.version.clone(),
            name: self.name.clone(),
            path: extract_dir.join(format!("{}-{}/", self.name, self.version)),
        }
    }
}

impl Crate {
    fn run_clippy_lints(&self, cargo_clippy_path: &PathBuf) -> Vec<String> {
        println!("Linting {} {}...", &self.name, &self.version);
        let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();

        let shared_target_dir = clippy_project_root().join("target/crater/shared_target_dir/");

        let all_output = std::process::Command::new(cargo_clippy_path)
            .env("CARGO_TARGET_DIR", shared_target_dir)
            // lint warnings will look like this:
            // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
            .args(&[
                "--",
                "--message-format=json",
                "--",
                "--cap-lints=warn",
                "-Wclippy::pedantic",
                "-Wclippy::cargo",
            ])
            .current_dir(&self.path)
            .output()
            .unwrap();
        let stdout = String::from_utf8_lossy(&all_output.stdout);
        let output_lines = stdout.lines();
        //dbg!(&output_lines);
        let warnings: Vec<ClippyWarning> = output_lines
            .into_iter()
            // get all clippy warnings
            .filter(|line| line.contains("clippy::"))
            .map(|json_msg| parse_json_message(json_msg, &self))
            .collect();

        let mut output: Vec<String> = warnings.iter().map(|warning| warning.to_string()).collect();
        // sort messages alphabetically to avoid noise in the logs
        output.sort();
        output
    }
}

fn build_clippy() {
    Command::new("cargo")
        .arg("build")
        .output()
        .expect("Failed to build clippy!");
}

// get a list of CrateSources we want to check from a "crater_crates.toml" file.
fn read_crates() -> Vec<CrateSource> {
    let toml_path = PathBuf::from("clippy_dev/crater_crates.toml");
    let toml_content: String =
        std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
    let crate_list: CrateList =
        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
    // parse the hashmap of the toml file into a list of crates
    let tomlcrates: Vec<TomlCrate> = crate_list
        .crates
        .into_iter()
        .map(|(name, versions)| TomlCrate { name, versions })
        .collect();

    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
    // multiple Cratesources)
    let mut crate_sources = Vec::new();
    tomlcrates.into_iter().for_each(|tk| {
        tk.versions.iter().for_each(|ver| {
            crate_sources.push(CrateSource {
                name: tk.name.clone(),
                version: ver.to_string(),
            });
        })
    });
    crate_sources
}

// extract interesting data from a json lint message
fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
    let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e));

    ClippyWarning {
        crate_name: krate.name.to_string(),
        crate_version: krate.version.to_string(),
        file: jmsg["message"]["spans"][0]["file_name"]
            .to_string()
            .trim_matches('"')
            .into(),
        line: jmsg["message"]["spans"][0]["line_start"]
            .to_string()
            .trim_matches('"')
            .into(),
        column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"]
            .to_string()
            .trim_matches('"')
            .into(),
        linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(),
        message: jmsg["message"]["message"].to_string().trim_matches('"').into(),
    }
}

// the main fn
pub fn run() {
    let cargo_clippy_path: PathBuf = PathBuf::from("target/debug/cargo-clippy");

    println!("Compiling clippy...");
    build_clippy();
    println!("Done compiling");

    // assert that clippy is found
    assert!(
        cargo_clippy_path.is_file(),
        "target/debug/cargo-clippy binary not found! {}",
        cargo_clippy_path.display()
    );

    // download and extract the crates, then run clippy on them and collect clippys warnings

    let clippy_lint_results: Vec<Vec<String>> = read_crates()
        .into_iter()
        .map(|krate| krate.download_and_extract())
        .map(|krate| krate.run_clippy_lints(&cargo_clippy_path))
        .collect();

    let mut all_warnings: Vec<String> = clippy_lint_results.into_iter().flatten().collect();
    all_warnings.sort();

    // save the text into mini-crater/logs.txt
    let text = all_warnings.join("");
    write("mini-crater/logs.txt", text).unwrap();
}
<commit_msg>cargo dev crater: refactor to get a list of all ClippyWarnings<commit_after>// Run clippy on a fixed set of crates and collect the warnings.
// This helps observing the impact clippy changs have on a set of real-world code.
//
// When a new lint is introduced, we can search the results for new warnings and check for false
// positives.

#![allow(clippy::filter_map)]

use crate::clippy_project_root;

use std::collections::HashMap;
use std::process::Command;
use std::{fmt, fs::write, path::PathBuf};

use serde::{Deserialize, Serialize};
use serde_json::Value;

// use this to store the crates when interacting with the crates.toml file
#[derive(Debug, Serialize, Deserialize)]
struct CrateList {
    crates: HashMap<String, Vec<String>>,
}

// crate data we stored in the toml, can have multiple versions per crate
// A single TomlCrate is laster mapped to several CrateSources in that case
struct TomlCrate {
    name: String,
    versions: Vec<String>,
}

// represents an archive we download from crates.io
#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
struct CrateSource {
    name: String,
    version: String,
}

// represents the extracted sourcecode of a crate
#[derive(Debug)]
struct Crate {
    version: String,
    name: String,
    // path to the extracted sources that clippy can check
    path: PathBuf,
}

#[derive(Debug)]
struct ClippyWarning {
    crate_name: String,
    crate_version: String,
    file: String,
    line: String,
    column: String,
    linttype: String,
    message: String,
}

impl std::fmt::Display for ClippyWarning {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(
            f,
            r#"{}/{}/{}:{}:{} {} "{}""#,
            &self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message
        )
    }
}

impl CrateSource {
    fn download_and_extract(&self) -> Crate {
        let extract_dir = PathBuf::from("target/crater/crates");
        let krate_download_dir = PathBuf::from("target/crater/downloads");

        // url to download the crate from crates.io
        let url = format!(
            "https://crates.io/api/v1/crates/{}/{}/download",
            self.name, self.version
        );
        println!("Downloading and extracting {} {} from {}", self.name, self.version, url);
        let _ = std::fs::create_dir("target/crater/");
        let _ = std::fs::create_dir(&krate_download_dir);
        let _ = std::fs::create_dir(&extract_dir);

        let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", &self.name, &self.version));
        // don't download/extract if we already have done so
        if !krate_file_path.is_file() {
            // create a file path to download and write the crate data into
            let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
            let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
            // copy the crate into the file
            std::io::copy(&mut krate_req, &mut krate_dest).unwrap();

            // unzip the tarball
            let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
            // extract the tar archive
            let mut archive = tar::Archive::new(ungz_tar);
            archive.unpack(&extract_dir).expect("Failed to extract!");
        }
        // crate is extracted, return a new Krate object which contains the path to the extracted
        // sources that clippy can check
        Crate {
            version: self.version.clone(),
            name: self.name.clone(),
            path: extract_dir.join(format!("{}-{}/", self.name, self.version)),
        }
    }
}

impl Crate {
    fn run_clippy_lints(&self, cargo_clippy_path: &PathBuf) -> Vec<ClippyWarning> {
        println!("Linting {} {}...", &self.name, &self.version);
        let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();

        let shared_target_dir = clippy_project_root().join("target/crater/shared_target_dir/");

        let all_output = std::process::Command::new(cargo_clippy_path)
            .env("CARGO_TARGET_DIR", shared_target_dir)
            // lint warnings will look like this:
            // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter`
            .args(&[
                "--",
                "--message-format=json",
                "--",
                "--cap-lints=warn",
                "-Wclippy::pedantic",
                "-Wclippy::cargo",
            ])
            .current_dir(&self.path)
            .output()
            .unwrap();
        let stdout = String::from_utf8_lossy(&all_output.stdout);
        let output_lines = stdout.lines();
        //dbg!(&output_lines);
        let warnings: Vec<ClippyWarning> = output_lines
            .into_iter()
            // get all clippy warnings
            .filter(|line| line.contains("clippy::"))
            .map(|json_msg| parse_json_message(json_msg, &self))
            .collect();
        warnings
    }
}

fn build_clippy() {
    Command::new("cargo")
        .arg("build")
        .output()
        .expect("Failed to build clippy!");
}

// get a list of CrateSources we want to check from a "crater_crates.toml" file.
fn read_crates() -> Vec<CrateSource> {
    let toml_path = PathBuf::from("clippy_dev/crater_crates.toml");
    let toml_content: String =
        std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
    let crate_list: CrateList =
        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e));
    // parse the hashmap of the toml file into a list of crates
    let tomlcrates: Vec<TomlCrate> = crate_list
        .crates
        .into_iter()
        .map(|(name, versions)| TomlCrate { name, versions })
        .collect();

    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
    // multiple Cratesources)
    let mut crate_sources = Vec::new();
    tomlcrates.into_iter().for_each(|tk| {
        tk.versions.iter().for_each(|ver| {
            crate_sources.push(CrateSource {
                name: tk.name.clone(),
                version: ver.to_string(),
            });
        })
    });
    crate_sources
}

// extract interesting data from a json lint message
fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
    let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e));

    ClippyWarning {
        crate_name: krate.name.to_string(),
        crate_version: krate.version.to_string(),
        file: jmsg["message"]["spans"][0]["file_name"]
            .to_string()
            .trim_matches('"')
            .into(),
        line: jmsg["message"]["spans"][0]["line_start"]
            .to_string()
            .trim_matches('"')
            .into(),
        column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"]
            .to_string()
            .trim_matches('"')
            .into(),
        linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(),
        message: jmsg["message"]["message"].to_string().trim_matches('"').into(),
    }
}

// the main fn
pub fn run() {
    let cargo_clippy_path: PathBuf = PathBuf::from("target/debug/cargo-clippy");

    println!("Compiling clippy...");
    build_clippy();
    println!("Done compiling");

    // assert that clippy is found
    assert!(
        cargo_clippy_path.is_file(),
        "target/debug/cargo-clippy binary not found! {}",
        cargo_clippy_path.display()
    );

    // download and extract the crates, then run clippy on them and collect clippys warnings
    // flatten into one big list of warnings

    let clippy_warnings: Vec<ClippyWarning> = read_crates()
        .into_iter()
        .map(|krate| krate.download_and_extract())
        .map(|krate| krate.run_clippy_lints(&cargo_clippy_path))
        .flatten()
        .collect();

    let all_msgs: Vec<String> = clippy_warnings.iter().map(|warning| warning.to_string()).collect();

    // save the text into mini-crater/logs.txt
    let text = all_msgs.join("");
    write("mini-crater/logs.txt", text).unwrap();
}
<|endoftext|>


<commit_before>/// If you are using Rust 2018 no `external crate byteorder;` is necessary

// The `gen` module exists because `cargo test` fails
// if `j1939.rs` is directly in the examples folder
// (since it's treated like an example and a `main` is expected).
// Usually you could directly use `mod j1939;` here.
mod gen;

/// Generated module
use crate::gen::j1939;

fn main() {
    // J1939 - Operators External Light Controls Message Id
    let can_message_id = 2365443326u32;
    // can frame data field (0-8 bytes)
    let can_frame_data: &[u8] = &[0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    // CAN Message ID constant from generated code
    if can_message_id == j1939::MESSAGE_ID_OEL {
        // J1939 - Operators External Light Controls Message
        let oel = j1939::Oel::new(can_frame_data);

        match oel.hazardlightswitch() {
            j1939::HazardLightSwitch2365443326::HazardLampsToBeFlashing => println!("Hazard Lamps To Be Flashing"),
            j1939::HazardLightSwitch2365443326::HazardLampsToBeOff => println!("Hazard Lamps To Be Off"),
            j1939::HazardLightSwitch2365443326::NotAvailable => println!("Not available"),
            j1939::HazardLightSwitch2365443326::Error => println!("Error"),
            j1939::HazardLightSwitch2365443326::XValue(_) => unreachable!(),
        }
    }
}<commit_msg>Fix dbcc demo<commit_after>/// If you are using Rust 2018 no `external crate byteorder;` is necessary

// The `gen` module exists because `cargo test` fails
// if `j1939.rs` is directly in the examples folder
// (since it's treated like an example and a `main` is expected).
// Usually you could directly use `mod j1939;` here.
mod gen;

/// Generated module
use crate::gen::j1939;

fn main() {
    // J1939 - Operators External Light Controls Message Id
    let can_message_id = 2365443326u32;
    // can frame data field (0-8 bytes)
    let can_frame_data: Vec<u8> = vec![0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    // CAN Message ID constant from generated code
    if can_message_id == j1939::MESSAGE_ID_OEL {
        // J1939 - Operators External Light Controls Message
        let oel = j1939::Oel::new(can_frame_data);

        match oel.hazardlightswitch() {
            j1939::HazardLightSwitch2365443326::HazardLampsToBeFlashing => println!("Hazard Lamps To Be Flashing"),
            j1939::HazardLightSwitch2365443326::HazardLampsToBeOff => println!("Hazard Lamps To Be Off"),
            j1939::HazardLightSwitch2365443326::NotAvailable => println!("Not available"),
            j1939::HazardLightSwitch2365443326::Error => println!("Error"),
            j1939::HazardLightSwitch2365443326::XValue(_) => unreachable!(),
        }
    }
}<|endoftext|>


<commit_before>use std::str;
use std::sync::Arc;
use std::convert::From;

use futures::{BoxFuture, Future};
use tokio_core::io::Io;
use minihttp::{Error, Request};
use minihttp::enums::Header;
use minihttp::client::{Response, HttpClient};
use tk_bufstream::IoBuf;

use config::proxy::Proxy;
use {Pickler};

pub enum ProxyCall {
    Prepare {
        hostport: String,
        settings: Arc<Proxy>,
    },
    Ready {
        response: Response,
    },
}

/// Build and return curl.Easy handle.
///
/// Response body will be written into `resp_bu``.
/// `headers_counter` will hold number of response headers parsed by curl.
pub fn prepare(mut request: Request, hostport: String,
               settings: Arc<Proxy>,
               client: &mut HttpClient)
{
    client.request(
        request.method,
        format!("http://{}{}", hostport, request.path).as_str());

    let ip_addr = format!("{}", request.peer_addr.ip());

    if let Some(ref ip_header) = settings.ip_header {
        client.add_header(Header::from(&ip_header[..]), ip_addr.as_str());
    }

    // Copy headers
    for &(ref name, ref value) in &request.headers {
        // TODO: validate headers
        match name {
            &Header::Host => client.add_header(Header::Host, value),
            &Header::Raw(ref name) => client.add_header(
                Header::from(name.as_str()), value),
            _ => continue,
        };
    }
    if request.body.is_some() {
        let body = request.body.take().unwrap();
        let clen = body.data.len();
        client.add_length(clen as u64);
        client.done_headers();
        client.body_from_buf(body.data);
    } else {
        client.done_headers();
    }
}

/// Serialize buffered response.
///
pub fn serialize<S>(mut response: Pickler<S>, resp: Response)
    -> BoxFuture<IoBuf<S>, Error>
    where S: Io + Send + 'static,
{
    // TODO: handle response codes respectively,
    //      ie 204 has no body.
    response.status(resp.status.clone());

    if resp.body.is_some() {
        if let Some(len) = resp.content_length() {
            response.add_length(len);
        }
    }
    for (ref header, ref value) in resp.headers {
        match header {
            &Header::TransferEncoding => {
                println!("Adding Chunked response");
                response.add_chunked();
            }
            &Header::Raw(ref name) => {
                response.add_header(name.as_str(), value);
            }
            _ => {} // ignore
        }
    }

    if response.done_headers() {
        if resp.status.response_has_body() {
            if let Some(ref body) = resp.body {
                response.write_body(&body[..]);
            }
        }
    };
    response.done().boxed()
}
<commit_msg>Remove debugging message<commit_after>use std::str;
use std::sync::Arc;
use std::convert::From;

use futures::{BoxFuture, Future};
use tokio_core::io::Io;
use minihttp::{Error, Request};
use minihttp::enums::Header;
use minihttp::client::{Response, HttpClient};
use tk_bufstream::IoBuf;

use config::proxy::Proxy;
use {Pickler};

pub enum ProxyCall {
    Prepare {
        hostport: String,
        settings: Arc<Proxy>,
    },
    Ready {
        response: Response,
    },
}

/// Build and return curl.Easy handle.
///
/// Response body will be written into `resp_bu``.
/// `headers_counter` will hold number of response headers parsed by curl.
pub fn prepare(mut request: Request, hostport: String,
               settings: Arc<Proxy>,
               client: &mut HttpClient)
{
    client.request(
        request.method,
        format!("http://{}{}", hostport, request.path).as_str());

    let ip_addr = format!("{}", request.peer_addr.ip());

    if let Some(ref ip_header) = settings.ip_header {
        client.add_header(Header::from(&ip_header[..]), ip_addr.as_str());
    }

    // Copy headers
    for &(ref name, ref value) in &request.headers {
        // TODO: validate headers
        match name {
            &Header::Host => client.add_header(Header::Host, value),
            &Header::Raw(ref name) => client.add_header(
                Header::from(name.as_str()), value),
            _ => continue,
        };
    }
    if request.body.is_some() {
        let body = request.body.take().unwrap();
        let clen = body.data.len();
        client.add_length(clen as u64);
        client.done_headers();
        client.body_from_buf(body.data);
    } else {
        client.done_headers();
    }
}

/// Serialize buffered response.
///
pub fn serialize<S>(mut response: Pickler<S>, resp: Response)
    -> BoxFuture<IoBuf<S>, Error>
    where S: Io + Send + 'static,
{
    // TODO: handle response codes respectively,
    //      ie 204 has no body.
    response.status(resp.status.clone());

    if resp.body.is_some() {
        if let Some(len) = resp.content_length() {
            response.add_length(len);
        }
    }
    for (ref header, ref value) in resp.headers {
        match header {
            &Header::TransferEncoding => {
                response.add_chunked();
            }
            &Header::Raw(ref name) => {
                response.add_header(name.as_str(), value);
            }
            _ => {} // ignore
        }
    }

    if response.done_headers() {
        if resp.status.response_has_body() {
            if let Some(ref body) = resp.body {
                response.write_body(&body[..]);
            }
        }
    };
    response.done().boxed()
}
<|endoftext|>


<commit_before>use serialize::{json, Decodable, Decoder};
use serialize::json::{Object, List, I64, U64, F64, Boolean, String};
use document::{SolrDocument, SolrField, SolrString, SolrI64, SolrU64, SolrF64, SolrBoolean, SolrNull};

pub type SolrUpdateResult = Result<SolrUpdateResponse, SolrError>;
pub type SolrQueryResult = Result<SolrQueryResponse, SolrError>;

/// SolrError
pub struct SolrError {
    /// HTTP status.
    /// When failed to connect, it will be 0 (zero).
    pub status: int,
    /// Time it took to execute the request in milliseconds
    pub time: int,
    /// Detailed error message
    pub message: String
}

impl<D: Decoder<E>, E> Decodable<D, E> for SolrError {
    fn decode(d: &mut D) -> Result<SolrError, E> {
        d.read_struct("root", 0, |d| {
            d.read_struct_field("error", 0, |d| {
                Ok(SolrError{
                    message: try!(d.read_struct_field("msg", 0, Decodable::decode)),
                    status: try!(d.read_struct_field("code", 1, Decodable::decode)),
                    // TODO: implement time parsing from request header
                    time: 0})
            })
        })
    }
}

/// Solr response used for update/indexing/commit operations
pub struct SolrUpdateResponse {
    /// HTTP status.
    /// When failed to connect, it will be 0 (zero).
    pub status: int,
    /// Time it took to execute the request in milliseconds
    pub time: int
}

impl<D: Decoder<E>, E> Decodable<D, E> for SolrUpdateResponse {
    fn decode(d: &mut D) -> Result<SolrUpdateResponse, E> {
        d.read_struct("root", 0, |d| {
            d.read_struct_field("responseHeader", 0, |d| {
                Ok(SolrUpdateResponse{
                    status: try!(d.read_struct_field("status", 0, Decodable::decode)),
                    time: try!(d.read_struct_field("QTime", 1, Decodable::decode))
                })
            })
        })
    }
}

/// Solr query response
#[deriving(Show)]
pub struct SolrQueryResponse {
    /// HTTP status.
    /// When failed to connect, it will be 0 (zero).
    pub status: u32,
    /// Time it took to execute the request in milliseconds
    pub time: u32,
    /// Total number of rows found.
    /// Note that this will probably be different from returned subset of rows,
    /// because Solr will always use pagination
    pub total: u64,
    /// Rows offset (zero based)
    pub start: u64,
    /// Current page of found Solr documents
    pub items: Vec<SolrDocument>
}

/*
Example JSON of query response:
```ignore
{
  "responseHeader": {
    "status": 0,
    "QTime": 1
  },
  "response": {
    "numFound": 57,
    "start": 0,
    "docs": [
      {
        "id": 1,
        "_version_": "1478235317501689856"
      },
      {
        "id": 3,
        "_version_": "1478235317504835584"
      }
    ]
  }
}
*/
impl SolrQueryResponse {
    /// Deserializes SolrQueryResponse from JSON string
    pub fn from_json_str(json_str: &str) -> SolrQueryResult {
        let mut response = SolrQueryResponse{status: 0, time: 0, total: 0, start: 0, items: Vec::new()};
        let mut error: String = "".to_string();
        match json::from_str(json_str) {
            Ok(json) => match json {
               Object(tree_map) => {
                    match tree_map.find(&"responseHeader".to_string()) {
                        Some(rh) => {
                            match rh.find(&"QTime".to_string()){
                                Some(time_json) => response.time = time_json.as_i64().unwrap() as u32,
                                None => error = "SolrQueryResponse JSON parsing error (responseHeader): QTime not found".to_string()
                            }
                            match rh.find(&"status".to_string()) {
                                Some(status_json) => response.status = status_json.as_u64().unwrap() as u32,
                                None => error = "SolrQueryResponse JSON parsing error (responseHeader): status not found".to_string()
                            }
                        },
                        None => error = "SolrQueryResponse JSON parsing error: responseHeader not found".to_string()

                    }
                    match tree_map.find(&"response".to_string()) {
                        Some(rs) => {
                            match rs.find(&"numFound".to_string()){
                                Some(total_json) => response.total = total_json.as_u64().unwrap(),
                                None => error = "SolrQueryResponse JSON parsing error (response): numFound not found".to_string()
                            }
                            match rs.find(&"start".to_string()) {
                                Some(start_json) => response.start = start_json.as_u64().unwrap(),
                                None => error = "SolrQueryResponse JSON parsing error (response): start not found".to_string()
                            }
                            match rs.find(&"docs".to_string()){
                                Some(docs_json) => {
                                    match docs_json {
                                        & List(ref docs) => {
                                            for doc_json in docs.iter() {
                                                match doc_json {
                                                    & Object(ref tm) => {
                                                        let mut doc = SolrDocument{fields: Vec::with_capacity(tm.len())};
                                                        for (k, json_v) in tm.iter() {
                                                            let v = match json_v {
                                                                & I64(i64) => SolrI64(i64),
                                                                & U64(u64) => SolrU64(u64),
                                                                & F64(f64) => SolrF64(f64),
                                                                & String(ref string) => SolrString(string.clone()),
                                                                & Boolean(bool) => SolrBoolean(bool),
                                                                _ => SolrNull
                                                            };
                                                            doc.fields.push(SolrField{name: k.clone(), value: v});
                                                        }
                                                        response.items.push(doc);
                                                    },
                                                    _ => error = "SolrQueryResponse JSON parsing error (response => docs): doc is not an object".to_string()
                                                }

                                            }
                                        },
                                        _ =>  error = "SolrQueryResponse JSON parsing error (response): docs is not a JSON list".to_string()
                                    }
                                },
                                None => error = "SolrQueryResponse JSON parsing error (response): docs not found".to_string()
                            }
                        },
                        None => error = "SolrQueryResponse JSON parsing error: response not found".to_string()
                    }
               },
               _ => error = "SolrQueryResponse JSON parsing error: query response is not a JSON object.".to_string()
            },
            Err(e) => error = format!("SolrQueryResponse JSON parsing error: {}", e).to_string()
        }
        if error.len() == 0 {
            Ok(response)
        } else {
            Err(SolrError{time: 0, status: 0, message: error})
        }
    }
}
<commit_msg>Refactoring: split out chunks of code from a huge method<commit_after>use serialize::{json, Decodable, Decoder};
use serialize::json::{Json, Object, List, I64, U64, F64, Boolean, String};
use document::{SolrDocument, SolrField, SolrString, SolrI64, SolrU64, SolrF64, SolrBoolean, SolrNull};

pub type SolrUpdateResult = Result<SolrUpdateResponse, SolrError>;
pub type SolrQueryResult = Result<SolrQueryResponse, SolrError>;

/// SolrError
pub struct SolrError {
    /// HTTP status.
    /// When failed to connect, it will be 0 (zero).
    pub status: int,
    /// Time it took to execute the request in milliseconds
    pub time: int,
    /// Detailed error message
    pub message: String
}

impl<D: Decoder<E>, E> Decodable<D, E> for SolrError {
    fn decode(d: &mut D) -> Result<SolrError, E> {
        d.read_struct("root", 0, |d| {
            d.read_struct_field("error", 0, |d| {
                Ok(SolrError{
                    message: try!(d.read_struct_field("msg", 0, Decodable::decode)),
                    status: try!(d.read_struct_field("code", 1, Decodable::decode)),
                    // TODO: implement time parsing from request header
                    time: 0})
            })
        })
    }
}

/// Solr response used for update/indexing/commit operations
pub struct SolrUpdateResponse {
    /// HTTP status.
    /// When failed to connect, it will be 0 (zero).
    pub status: int,
    /// Time it took to execute the request in milliseconds
    pub time: int
}

impl<D: Decoder<E>, E> Decodable<D, E> for SolrUpdateResponse {
    fn decode(d: &mut D) -> Result<SolrUpdateResponse, E> {
        d.read_struct("root", 0, |d| {
            d.read_struct_field("responseHeader", 0, |d| {
                Ok(SolrUpdateResponse{
                    status: try!(d.read_struct_field("status", 0, Decodable::decode)),
                    time: try!(d.read_struct_field("QTime", 1, Decodable::decode))
                })
            })
        })
    }
}

/// Solr query response
#[deriving(Show)]
pub struct SolrQueryResponse {
    /// HTTP status.
    /// When failed to connect, it will be 0 (zero).
    pub status: u32,
    /// Time it took to execute the request in milliseconds
    pub time: u32,
    /// Total number of rows found.
    /// Note that this will probably be different from returned subset of rows,
    /// because Solr will always use pagination
    pub total: u64,
    /// Rows offset (zero based)
    pub start: u64,
    /// Current page of found Solr documents
    pub items: Vec<SolrDocument>
}

/*
Example JSON of query response:
```ignore
{
  "responseHeader": {
    "status": 0,
    "QTime": 1
  },
  "response": {
    "numFound": 57,
    "start": 0,
    "docs": [
      {
        "id": 1,
        "_version_": "1478235317501689856"
      },
      {
        "id": 3,
        "_version_": "1478235317504835584"
      }
    ]
  }
}
*/
impl SolrQueryResponse {
    /// Deserializes SolrQueryResponse from JSON string
    pub fn from_json_str(json_str: &str) -> SolrQueryResult {
        let mut response = SolrQueryResponse{status: 0, time: 0, total: 0, start: 0, items: Vec::new()};
        let mut error: String = "".to_string();
        match json::from_str(json_str) {
            Ok(json) => match json {
               Object(tree_map) => {
                    match tree_map.find(&"responseHeader".to_string()) {
                        Some(rh) => {
                            match rh.find(&"QTime".to_string()){
                                Some(time_json) => response.time = time_json.as_i64().unwrap() as u32,
                                None => error = "SolrQueryResponse JSON parsing error (responseHeader): QTime not found".to_string()
                            }
                            match rh.find(&"status".to_string()) {
                                Some(status_json) => response.status = status_json.as_u64().unwrap() as u32,
                                None => error = "SolrQueryResponse JSON parsing error (responseHeader): status not found".to_string()
                            }
                        },
                        None => error = "SolrQueryResponse JSON parsing error: responseHeader not found".to_string()

                    }
                    match tree_map.find(&"response".to_string()) {
                        Some(rs) => {
                            match rs.find(&"numFound".to_string()){
                                Some(total_json) => response.total = total_json.as_u64().unwrap(),
                                None => error = "SolrQueryResponse JSON parsing error (response): numFound not found".to_string()
                            }
                            match rs.find(&"start".to_string()) {
                                Some(start_json) => response.start = start_json.as_u64().unwrap(),
                                None => error = "SolrQueryResponse JSON parsing error (response): start not found".to_string()
                            }
                            match rs.find(&"docs".to_string()){
                                Some(docs_json) => {
                                    match docs_json {
                                        & List(ref docs) => {
                                            for doc_json in docs.iter() {
                                                match SolrQueryResponse::parse_doc(doc_json){
                                                    Ok(doc) => response.items.push(doc),
                                                    Err(e) => error = e
                                                }

                                            }
                                        },
                                        _ =>  error = "SolrQueryResponse JSON parsing error (response): docs is not a JSON list".to_string()
                                    }
                                },
                                None => error = "SolrQueryResponse JSON parsing error (response): docs not found".to_string()
                            }
                        },
                        None => error = "SolrQueryResponse JSON parsing error: response not found".to_string()
                    }
               },
               _ => error = "SolrQueryResponse JSON parsing error: query response is not a JSON object.".to_string()
            },
            Err(e) => error = format!("SolrQueryResponse JSON parsing error: {}", e).to_string()
        }
        if error.len() == 0 {
            Ok(response)
        } else {
            Err(SolrError{time: 0, status: 0, message: error})
        }
    }

    fn parse_doc(doc_json: &Json) -> Result<SolrDocument, String> {
        match doc_json {
            & Object(ref tm) => {
                let mut doc = SolrDocument{fields: Vec::with_capacity(tm.len())};
                for (k, json_v) in tm.iter() {
                    let v = match json_v {
                        & I64(i64) => SolrI64(i64),
                        & U64(u64) => SolrU64(u64),
                        & F64(f64) => SolrF64(f64),
                        & String(ref string) => SolrString(string.clone()),
                        & Boolean(bool) => SolrBoolean(bool),
                        _ => SolrNull
                    };
                    doc.fields.push(SolrField{name: k.clone(), value: v});
                }
                Ok(doc)
            },
            _ => Err("SolrQueryResponse JSON parsing error (response => docs): doc is not an object".to_string())
        }
    }
}
<|endoftext|>


<commit_before>use std::c_str::CString;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link(kind="framework", name="SDL2")]
    extern {}

    #[cfg(not(mac_framework))]
    #[link(name="SDL2")]
    extern {}
}

#[cfg(target_os="win32")]
#[cfg(target_os="linux")]
#[cfg(target_os="freebsd")]
mod others {
    #[link(name="SDL2")]
    extern {}
}

#[allow(non_camel_case_types)]
pub mod ll {
    use libc::{c_int, c_uint, c_char, uint32_t};

    pub type SDL_errorcode = c_uint;
    pub static SDL_ENOMEM: SDL_errorcode = 0;
    pub static SDL_EFREAD: SDL_errorcode = 1;
    pub static SDL_EFWRITE: SDL_errorcode = 2;
    pub static SDL_EFSEEK: SDL_errorcode = 3;
    pub static SDL_UNSUPPORTED: SDL_errorcode = 4;
    pub static SDL_LASTERROR: SDL_errorcode = 5;

    pub type SDL_InitFlag = uint32_t;
    pub static SDL_INIT_TIMER: SDL_InitFlag = 0x00000001;
    pub static SDL_INIT_AUDIO: SDL_InitFlag = 0x00000010;
    pub static SDL_INIT_VIDEO: SDL_InitFlag = 0x00000020;
    pub static SDL_INIT_JOYSTICK: SDL_InitFlag = 0x00000200;
    pub static SDL_INIT_HAPTIC: SDL_InitFlag = 0x00001000;
    pub static SDL_INIT_GAMECONTROLLER: SDL_InitFlag = 0x00002000;
    pub static SDL_INIT_EVENTS: SDL_InitFlag = 0x00004000;
    pub static SDL_INIT_NOPARACHUTE: SDL_InitFlag = 0x00100000;
    pub static SDL_INIT_EVERYTHING: SDL_InitFlag = 0x0000FFFF;

    //SDL_error.h
    extern "C" {
        pub fn SDL_ClearError();
        pub fn SDL_Error(code: SDL_errorcode) -> c_int;
        pub fn SDL_SetError(fmt: *const c_char) -> c_int;
        pub fn SDL_GetError() -> *const c_char;

        //SDL.h
        pub fn SDL_Init(flags: uint32_t) -> c_int;
        pub fn SDL_InitSubSystem(flags: SDL_InitFlag) -> c_int;
        pub fn SDL_QuitSubSystem(flags: SDL_InitFlag);
        pub fn SDL_WasInit(flags: SDL_InitFlag) -> SDL_InitFlag;
        pub fn SDL_Quit();

        //SDL_timer.h
        pub fn SDL_GetTicks() -> uint32_t;
    }
}

bitflags!(flags InitFlag: u32 {
    static InitTimer = ll::SDL_INIT_TIMER,
    static InitAudio = ll::SDL_INIT_AUDIO,
    static InitVideo = ll::SDL_INIT_VIDEO,
    static InitJoystick = ll::SDL_INIT_JOYSTICK,
    static InitHaptic = ll::SDL_INIT_HAPTIC,
    static InitGameController = ll::SDL_INIT_GAMECONTROLLER,
    static InitEvents = ll::SDL_INIT_EVENTS,
    static InitNoParachute = ll::SDL_INIT_NOPARACHUTE,
    static InitEverything = ll::SDL_INIT_EVERYTHING
})

#[deriving(PartialEq)]
pub enum Error {
    NoMemError = ll::SDL_ENOMEM as int,
    ReadError = ll::SDL_EFREAD as int,
    WriteError = ll::SDL_EFWRITE as int,
    SeekError = ll::SDL_EFSEEK as int,
    UnsupportedError = ll::SDL_UNSUPPORTED as int
}

pub type SdlResult<T> = Result<T, String>;

pub fn init(flags: InitFlag) -> bool {
    unsafe {
        ll::SDL_Init(flags.bits()) == 0
    }
}

pub fn init_subsystem(flags: InitFlag) -> bool {
    unsafe {
        ll::SDL_InitSubSystem(flags.bits()) == 0
    }
}

pub fn quit_subsystem(flags: InitFlag) {
    unsafe { ll::SDL_QuitSubSystem(flags.bits()); }
}

pub fn quit() {
    unsafe { ll::SDL_Quit(); }
}

pub fn was_inited(flags: InitFlag) -> InitFlag {
    unsafe {
        let raw = ll::SDL_WasInit(flags.bits());
        flags & InitFlag::from_bits(raw).unwrap()
    }
}

pub fn get_error() -> String {
    unsafe {
        let cstr = CString::new(ll::SDL_GetError(), false);
        cstr.as_str().unwrap().into_string()
    }
}

pub fn set_error(err: &str) {
    err.with_c_str(|buf| {
        unsafe { ll::SDL_SetError(buf); }
    })
}

pub fn set_error_from_code(err: Error) {
    unsafe { ll::SDL_Error(err as ll::SDL_errorcode); }
}

pub fn clear_error() {
    unsafe { ll::SDL_ClearError(); }
}

pub fn get_ticks() -> uint {
    unsafe { ll::SDL_GetTicks() as uint }
}
<commit_msg>Changed target_os from "win32" to "windows"<commit_after>use std::c_str::CString;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link(kind="framework", name="SDL2")]
    extern {}

    #[cfg(not(mac_framework))]
    #[link(name="SDL2")]
    extern {}
}

#[cfg(target_os="windows")]
#[cfg(target_os="linux")]
#[cfg(target_os="freebsd")]
mod others {
    #[link(name="SDL2")]
    extern {}
}

#[allow(non_camel_case_types)]
pub mod ll {
    use libc::{c_int, c_uint, c_char, uint32_t};

    pub type SDL_errorcode = c_uint;
    pub static SDL_ENOMEM: SDL_errorcode = 0;
    pub static SDL_EFREAD: SDL_errorcode = 1;
    pub static SDL_EFWRITE: SDL_errorcode = 2;
    pub static SDL_EFSEEK: SDL_errorcode = 3;
    pub static SDL_UNSUPPORTED: SDL_errorcode = 4;
    pub static SDL_LASTERROR: SDL_errorcode = 5;

    pub type SDL_InitFlag = uint32_t;
    pub static SDL_INIT_TIMER: SDL_InitFlag = 0x00000001;
    pub static SDL_INIT_AUDIO: SDL_InitFlag = 0x00000010;
    pub static SDL_INIT_VIDEO: SDL_InitFlag = 0x00000020;
    pub static SDL_INIT_JOYSTICK: SDL_InitFlag = 0x00000200;
    pub static SDL_INIT_HAPTIC: SDL_InitFlag = 0x00001000;
    pub static SDL_INIT_GAMECONTROLLER: SDL_InitFlag = 0x00002000;
    pub static SDL_INIT_EVENTS: SDL_InitFlag = 0x00004000;
    pub static SDL_INIT_NOPARACHUTE: SDL_InitFlag = 0x00100000;
    pub static SDL_INIT_EVERYTHING: SDL_InitFlag = 0x0000FFFF;

    //SDL_error.h
    extern "C" {
        pub fn SDL_ClearError();
        pub fn SDL_Error(code: SDL_errorcode) -> c_int;
        pub fn SDL_SetError(fmt: *const c_char) -> c_int;
        pub fn SDL_GetError() -> *const c_char;

        //SDL.h
        pub fn SDL_Init(flags: uint32_t) -> c_int;
        pub fn SDL_InitSubSystem(flags: SDL_InitFlag) -> c_int;
        pub fn SDL_QuitSubSystem(flags: SDL_InitFlag);
        pub fn SDL_WasInit(flags: SDL_InitFlag) -> SDL_InitFlag;
        pub fn SDL_Quit();

        //SDL_timer.h
        pub fn SDL_GetTicks() -> uint32_t;
    }
}

bitflags!(flags InitFlag: u32 {
    static InitTimer = ll::SDL_INIT_TIMER,
    static InitAudio = ll::SDL_INIT_AUDIO,
    static InitVideo = ll::SDL_INIT_VIDEO,
    static InitJoystick = ll::SDL_INIT_JOYSTICK,
    static InitHaptic = ll::SDL_INIT_HAPTIC,
    static InitGameController = ll::SDL_INIT_GAMECONTROLLER,
    static InitEvents = ll::SDL_INIT_EVENTS,
    static InitNoParachute = ll::SDL_INIT_NOPARACHUTE,
    static InitEverything = ll::SDL_INIT_EVERYTHING
})

#[deriving(PartialEq)]
pub enum Error {
    NoMemError = ll::SDL_ENOMEM as int,
    ReadError = ll::SDL_EFREAD as int,
    WriteError = ll::SDL_EFWRITE as int,
    SeekError = ll::SDL_EFSEEK as int,
    UnsupportedError = ll::SDL_UNSUPPORTED as int
}

pub type SdlResult<T> = Result<T, String>;

pub fn init(flags: InitFlag) -> bool {
    unsafe {
        ll::SDL_Init(flags.bits()) == 0
    }
}

pub fn init_subsystem(flags: InitFlag) -> bool {
    unsafe {
        ll::SDL_InitSubSystem(flags.bits()) == 0
    }
}

pub fn quit_subsystem(flags: InitFlag) {
    unsafe { ll::SDL_QuitSubSystem(flags.bits()); }
}

pub fn quit() {
    unsafe { ll::SDL_Quit(); }
}

pub fn was_inited(flags: InitFlag) -> InitFlag {
    unsafe {
        let raw = ll::SDL_WasInit(flags.bits());
        flags & InitFlag::from_bits(raw).unwrap()
    }
}

pub fn get_error() -> String {
    unsafe {
        let cstr = CString::new(ll::SDL_GetError(), false);
        cstr.as_str().unwrap().into_string()
    }
}

pub fn set_error(err: &str) {
    err.with_c_str(|buf| {
        unsafe { ll::SDL_SetError(buf); }
    })
}

pub fn set_error_from_code(err: Error) {
    unsafe { ll::SDL_Error(err as ll::SDL_errorcode); }
}

pub fn clear_error() {
    unsafe { ll::SDL_ClearError(); }
}

pub fn get_ticks() -> uint {
    unsafe { ll::SDL_GetTicks() as uint }
}
<|endoftext|>


<commit_before>use addressable::Addressable;
use addressable::AddressableUtil;

// General information on 65xx: http://en.wikipedia.org/wiki/MOS_Technology_6510
// Web simulator and much info: http://e-tradition.net/bytes/6502/
// Good reference and overview: http://www.obelisk.demon.co.uk/index.html

static NMI_VECTOR: u16 = 0xfffa;
static RESET_VECTOR: u16 = 0xfffc;
static IRQ_VECTOR: u16 = 0xfffe;

enum Operand {
	Implied,							// OPC				// operand implied
	Immediate(u8),						// OPC #$BB			// operand is byte (BB)
	Accumulator,						// OPC A			// operand is AC
	Relative(i8),						// OPC $BB			// branch target is PC + offset (BB), bit 7 signifies negative offset
	Absolute(u16),						// OPC $HHLL		// operand is address $HHLL
	AbsoluteIndexedWithX(u16),			// OPC $HHLL,X		// operand is address $HHLL incremented by X with carry
	AbsoluteIndexedWithY(u16),			// OPC $HHLL,Y		// operand is address $HHLL incremented by Y with carry
	Indirect(u16),						// OPC ($HHLL)		// operand is effective address; effective address is value of address
	ZeroPage(u8),						// OPC $LL			// operand is address $00LL
	ZeroPageIndexedWithX(u8),			// OPC $LL,X		// operand is address $00LL incremented by X, no page transition
	ZeroPageIndexedWithY(u8),			// OPC $LL,Y		// operand is address $00LL incremented by Y, no page transition
	ZeroPageIndexedWithXIndirect(u8),	// OPC ($LL,X)		// operand is effective address; effective address is $00LL incremented by X without carry
	ZeroPageIndirectIndexedWithY(u8),	// OPC ($LL),Y		// operand is effective address incremented by Y with carry; effective address is word at $00LL
}

impl Operand {
	fn addr<M: Addressable<u16>> (&self, cpu: &Mos6502<M>) -> u16 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand is never targetted to an address"),
			Immediate(_)						=> fail!("mos6510: Immediade operand is never targetted to an address"),
			Accumulator							=> fail!("mos6510: Accumulator operand is never targetted to an address"),
			Relative(offset)					=> cpu.pc + offset as u16,
			Absolute(addr)						=> addr,
			AbsoluteIndexedWithX(addr)			=> addr + cpu.x as u16,
			AbsoluteIndexedWithY(addr)			=> addr + cpu.y as u16,
			Indirect(addr)						=> cpu.mem.get_le(addr),
			ZeroPage(addr)						=> addr as u16,
			ZeroPageIndexedWithX(addr)			=> (addr + cpu.x) as u16,						// no page transition
			ZeroPageIndexedWithY(addr)			=> (addr + cpu.y) as u16,						// no page transition
			ZeroPageIndexedWithXIndirect(addr)	=> cpu.mem.get_le((addr + cpu.x) as u16),		// no page transition
			ZeroPageIndirectIndexedWithY(addr)	=> { let iaddr: u16 = cpu.mem.get_le(addr as u16); iaddr + cpu.y as u16 },
		}
	}

	fn get<M: Addressable<u16>> (&self, cpu: &Mos6502<M>) -> u8 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never has a value"),
			Immediate(val)						=> val,
			Accumulator							=> cpu.ac,
			Relative(_target)					=> fail!("mos6510: Relative operand never has a value"),
			op									=> { let addr = op.addr(cpu); cpu.mem.get(addr) },
		}
	}

	fn set<M: Addressable<u16>> (&self, cpu: &mut Mos6502<M>, val: u8) {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never sets a value"),
			Immediate(_)						=> fail!("mos6510: Immediate operand never sets a value"),
			Accumulator							=> cpu.ac = val,
			Relative(_target)					=> fail!("mos6510: Relative operand never sets a value"),
			op									=> { let addr = op.addr(cpu); cpu.mem.set(addr, val); },
		}
	}
}

pub struct Mos6502 {
	priv pc: u16,						// program counter
	priv ac: u8,						// accumulator
	priv x: u8,							// x register
	priv y: u8,							// y register
	priv sr: u8,						// status register (NV-BDIZC: Negative, oVerflow, Break, Decimal, Interrupt, Zero, Carry)
	priv sp: u8,						// stack pointer
}

impl<M: Addressable<u16>> Mos6502<M> {
	pub fn new (mem: M) -> Mos6502<M> {
		Mos6502 {
			pc: 0,
			ac: 0,
			x: 0,
			y: 0,
			sr: 0,
			sp: 0,
		}
	}

	fn get_opcode (&self) -> u8 {
		self.mem.get(self.reg.pc)
	}

	fn get_argument<T: Int> (&self) -> T {
		self.mem.get_le(self.reg.pc + 1)
	}

	pub fn step (&mut self) -> uint {
		// TODO
		0
	}
}

pub struct Mos6510<M> {
	priv cpu: Mos6502<M>,					// Core CPU
	priv port_ddr: u8,						// CPU port data direction register
	priv port_dat: u8,						// CPU port data register
}

impl<M: Addressable<u16>> Mos6510<M> {
	pub fn new (mem: M) -> Mos6510<M> {
		// TODO: addresses $0000 (data direction) and $0001 (data) are hardwired for the processor I/O port
		Mos6510 { cpu: Mos6502::new(mem), port_ddr: 0, port_dat: 0 }
	}

	pub fn step (&mut self) -> uint {
		self.cpu.step()
	}
}
<commit_msg>Don't hold a reference to memory in CPU, pass it for each step since only one mutable reference can exist<commit_after>use addressable::Addressable;
use addressable::AddressableUtil;

// General information on 65xx: http://en.wikipedia.org/wiki/MOS_Technology_6510
// Web simulator and much info: http://e-tradition.net/bytes/6502/
// Good reference and overview: http://www.obelisk.demon.co.uk/index.html

static NMI_VECTOR: u16 = 0xfffa;
static RESET_VECTOR: u16 = 0xfffc;
static IRQ_VECTOR: u16 = 0xfffe;

enum Operand {
	Implied,							// OPC				// operand implied
	Immediate(u8),						// OPC #$BB			// operand is byte (BB)
	Accumulator,						// OPC A			// operand is AC
	Relative(i8),						// OPC $BB			// branch target is PC + offset (BB), bit 7 signifies negative offset
	Absolute(u16),						// OPC $HHLL		// operand is address $HHLL
	AbsoluteIndexedWithX(u16),			// OPC $HHLL,X		// operand is address $HHLL incremented by X with carry
	AbsoluteIndexedWithY(u16),			// OPC $HHLL,Y		// operand is address $HHLL incremented by Y with carry
	Indirect(u16),						// OPC ($HHLL)		// operand is effective address; effective address is value of address
	ZeroPage(u8),						// OPC $LL			// operand is address $00LL
	ZeroPageIndexedWithX(u8),			// OPC $LL,X		// operand is address $00LL incremented by X, no page transition
	ZeroPageIndexedWithY(u8),			// OPC $LL,Y		// operand is address $00LL incremented by Y, no page transition
	ZeroPageIndexedWithXIndirect(u8),	// OPC ($LL,X)		// operand is effective address; effective address is $00LL incremented by X without carry
	ZeroPageIndirectIndexedWithY(u8),	// OPC ($LL),Y		// operand is effective address incremented by Y with carry; effective address is word at $00LL
}

impl Operand {
	fn addr<M: Addressable<u16>> (&self, cpu: &Mos6502, mem: &M) -> u16 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand is never targetted to an address"),
			Immediate(_)						=> fail!("mos6510: Immediade operand is never targetted to an address"),
			Accumulator							=> fail!("mos6510: Accumulator operand is never targetted to an address"),
			Relative(offset)					=> cpu.pc + offset as u16,
			Absolute(addr)						=> addr,
			AbsoluteIndexedWithX(addr)			=> addr + cpu.x as u16,
			AbsoluteIndexedWithY(addr)			=> addr + cpu.y as u16,
			Indirect(addr)						=> mem.get_le(addr),
			ZeroPage(addr)						=> addr as u16,
			ZeroPageIndexedWithX(addr)			=> (addr + cpu.x) as u16,						// no page transition
			ZeroPageIndexedWithY(addr)			=> (addr + cpu.y) as u16,						// no page transition
			ZeroPageIndexedWithXIndirect(addr)	=> mem.get_le((addr + cpu.x) as u16),			// no page transition
			ZeroPageIndirectIndexedWithY(addr)	=> { let iaddr: u16 = mem.get_le(addr as u16); iaddr + cpu.y as u16 },
		}
	}

	fn get<M: Addressable<u16>> (&self, cpu: &Mos6502, mem: &M) -> u8 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never has a value"),
			Immediate(val)						=> val,
			Accumulator							=> cpu.ac,
			Relative(_target)					=> fail!("mos6510: Relative operand never has a value"),
			op									=> { let addr = op.addr(cpu, mem); mem.get(addr) },
		}
	}

	fn set<M: Addressable<u16>> (&self, cpu: &mut Mos6502, mem: &mut M, val: u8) {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never sets a value"),
			Immediate(_)						=> fail!("mos6510: Immediate operand never sets a value"),
			Accumulator							=> cpu.ac = val,
			Relative(_target)					=> fail!("mos6510: Relative operand never sets a value"),
			op									=> { let addr = op.addr(cpu, mem); mem.set(addr, val); },
		}
	}
}

pub struct Mos6502 {
	priv pc: u16,						// program counter
	priv ac: u8,						// accumulator
	priv x: u8,							// x register
	priv y: u8,							// y register
	priv sr: u8,						// status register (NV-BDIZC: Negative, oVerflow, Break, Decimal, Interrupt, Zero, Carry)
	priv sp: u8,						// stack pointer
}

impl Mos6502 {
	pub fn new () -> Mos6502 {
		Mos6502 { pc: 0, ac: 0, x: 0, y: 0, sr: 0, sp: 0 }
	}

	fn get_opcode<M: Addressable<u16>> (&self, mem: &M) -> u8 {
		mem.get(self.pc)
	}

	fn get_argument<M: Addressable<u16>, T: Int> (&self, mem: &M) -> T {
		mem.get_le(self.pc + 1)
	}

	pub fn step<M: Addressable<u16>> (&mut self, mem: &mut M) -> uint {
		// TODO
		0
	}
}

pub struct Mos6510 {
	priv cpu: Mos6502,						// Core CPU
	priv port_ddr: u8,						// CPU port data direction register
	priv port_dat: u8,						// CPU port data register
}

impl Mos6510 {
	pub fn new () -> Mos6510 {
		// TODO: addresses $0000 (data direction) and $0001 (data) are hardwired for the processor I/O port
		Mos6510 { cpu: Mos6502::new(), port_ddr: 0, port_dat: 0 }
	}

	pub fn step<M: Addressable<u16>> (&mut self, mem: &mut M) -> uint {
		self.cpu.step(mem)
	}
}
<|endoftext|>


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

use crate::methods::{build_registry, JsonRpcService, RpcRegistry};
use futures::future::join_all;
use libra_config::config::NodeConfig;
use libra_mempool::MempoolClientSender;
use libradb::LibraDBTrait;
use serde::Serialize;
use serde_json::{map::Map, Value};
use std::{net::SocketAddr, sync::Arc};
use tokio::runtime::{Builder, Runtime};
use warp::Filter;

/// Creates HTTP server (warp-based) that serves JSON RPC requests
/// Returns handle to corresponding Tokio runtime
pub fn bootstrap(
    address: SocketAddr,
    libra_db: Arc<dyn LibraDBTrait>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    let runtime = Builder::new()
        .thread_name("rpc-")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("[rpc] failed to create runtime");

    let registry = Arc::new(build_registry());
    let service = JsonRpcService::new(libra_db, mp_sender);

    let handler = warp::any()
        .and(warp::path::end())
        .and(warp::post())
        .and(warp::header::exact("content-type", "application/json"))
        .and(warp::body::json())
        .and(warp::any().map(move || service.clone()))
        .and(warp::any().map(move || Arc::clone(&registry)))
        .and_then(rpc_endpoint);

    let server = warp::serve(handler).run(address);
    runtime.handle().spawn(server);
    runtime
}

/// Creates JSON RPC endpoint by given node config
pub fn bootstrap_from_config(
    config: &NodeConfig,
    libra_db: Arc<dyn LibraDBTrait>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    bootstrap(config.rpc.address, libra_db, mp_sender)
}

/// JSON RPC entry point
/// Handles all incoming rpc requests
/// Performs routing based on methods defined in `registry`
async fn rpc_endpoint(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    if let Value::Array(requests) = data {
        // batch API call
        let futures = requests
            .into_iter()
            .map(|req| rpc_request_handler(req, service.clone(), Arc::clone(&registry)));
        let responses = join_all(futures).await;
        Ok(Box::new(warp::reply::json(&Value::Array(responses))))
    } else {
        // single API call
        let resp = rpc_request_handler(data, service, registry).await;
        Ok(Box::new(warp::reply::json(&resp)))
    }
}

/// Handler of single RPC request
/// Performs validation and executes corresponding rpc handler
async fn rpc_request_handler(
    req: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Value {
    let request: Map<String, Value>;
    let mut response = Map::new();

    // set defaults: protocol version to 2.0, request id to null
    response.insert("jsonrpc".to_string(), Value::String("2.0".to_string()));
    response.insert("id".to_string(), Value::Null);

    match req {
        Value::Object(data) => {
            request = data;
        }
        _ => {
            response.insert(
                "error".to_string(),
                JsonRpcError::invalid_request().serialize(),
            );
            return Value::Object(response);
        }
    }

    // parse request id
    match parse_request_id(&request) {
        Ok(request_id) => {
            response.insert("id".to_string(), request_id);
        }
        Err(err) => {
            response.insert("error".to_string(), err.serialize());
            return Value::Object(response);
        }
    };

    // verify protocol version
    if let Err(err) = verify_protocol(&request) {
        response.insert("error".to_string(), err.serialize());
        return Value::Object(response);
    }

    // parse parameters
    let params;
    match request.get("params") {
        Some(Value::Array(parameters)) => {
            params = parameters.to_vec();
        }
        _ => {
            response.insert(
                "error".to_string(),
                JsonRpcError::invalid_params().serialize(),
            );
            return Value::Object(response);
        }
    }

    // get rpc handler
    match request.get("method") {
        Some(Value::String(name)) => match registry.get(name) {
            Some(handler) => match handler(service, params).await {
                Ok(result) => {
                    response.insert("result".to_string(), result);
                }
                Err(err) => {
                    response.insert(
                        "error".to_string(),
                        JsonRpcError::internal_error(err.to_string()).serialize(),
                    );
                }
            },
            None => {
                response.insert(
                    "error".to_string(),
                    JsonRpcError::method_not_found().serialize(),
                );
            }
        },
        _ => {
            response.insert(
                "error".to_string(),
                JsonRpcError::invalid_request().serialize(),
            );
        }
    }

    Value::Object(response)
}

#[derive(Serialize)]
struct JsonRpcError {
    code: i16,
    message: String,
}

impl JsonRpcError {
    fn serialize(self) -> Value {
        serde_json::to_value(self).unwrap_or(Value::Null)
    }

    fn invalid_request() -> Self {
        Self {
            code: -32600,
            message: "Invalid Request".to_string(),
        }
    }

    fn invalid_params() -> Self {
        Self {
            code: -32602,
            message: "Invalid params".to_string(),
        }
    }

    fn method_not_found() -> Self {
        Self {
            code: -32601,
            message: "Method not found".to_string(),
        }
    }

    fn internal_error(message: String) -> Self {
        Self {
            code: -32000,
            message: format!("Server error: {}", message),
        }
    }
}

fn parse_request_id(request: &Map<String, Value>) -> Result<Value, JsonRpcError> {
    match request.get("id") {
        Some(req_id) => {
            if req_id.is_string() || req_id.is_number() || req_id.is_null() {
                Ok(req_id.clone())
            } else {
                Err(JsonRpcError::invalid_request())
            }
        }
        None => Ok(Value::Null),
    }
}

fn verify_protocol(request: &Map<String, Value>) -> Result<(), JsonRpcError> {
    if let Some(Value::String(protocol)) = request.get("jsonrpc") {
        if protocol == "2.0" {
            return Ok(());
        }
    }
    Err(JsonRpcError::invalid_request())
}
<commit_msg>[json-rpc] bind to socket before spawning server task<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::methods::{build_registry, JsonRpcService, RpcRegistry};
use futures::future::join_all;
use libra_config::config::NodeConfig;
use libra_mempool::MempoolClientSender;
use libradb::LibraDBTrait;
use serde::Serialize;
use serde_json::{map::Map, Value};
use std::{net::SocketAddr, sync::Arc};
use tokio::runtime::{Builder, Runtime};
use warp::Filter;

/// Creates HTTP server (warp-based) that serves JSON RPC requests
/// Returns handle to corresponding Tokio runtime
pub fn bootstrap(
    address: SocketAddr,
    libra_db: Arc<dyn LibraDBTrait>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    let runtime = Builder::new()
        .thread_name("rpc-")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("[rpc] failed to create runtime");

    let registry = Arc::new(build_registry());
    let service = JsonRpcService::new(libra_db, mp_sender);

    let handler = warp::any()
        .and(warp::path::end())
        .and(warp::post())
        .and(warp::header::exact("content-type", "application/json"))
        .and(warp::body::json())
        .and(warp::any().map(move || service.clone()))
        .and(warp::any().map(move || Arc::clone(&registry)))
        .and_then(rpc_endpoint);

    // Ensure that we actually bind to the socket first before spawning the
    // server tasks. This helps in tests to prevent races where a client attempts
    // to make a request before the server task is actually listening on the
    // socket.
    //
    // Note: we need to enter the runtime context first to actually bind, since
    //       tokio TcpListener can only be bound inside a tokio context.
    let server = runtime.enter(move || warp::serve(handler).bind(address));
    runtime.handle().spawn(server);
    runtime
}

/// Creates JSON RPC endpoint by given node config
pub fn bootstrap_from_config(
    config: &NodeConfig,
    libra_db: Arc<dyn LibraDBTrait>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    bootstrap(config.rpc.address, libra_db, mp_sender)
}

/// JSON RPC entry point
/// Handles all incoming rpc requests
/// Performs routing based on methods defined in `registry`
async fn rpc_endpoint(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    if let Value::Array(requests) = data {
        // batch API call
        let futures = requests
            .into_iter()
            .map(|req| rpc_request_handler(req, service.clone(), Arc::clone(&registry)));
        let responses = join_all(futures).await;
        Ok(Box::new(warp::reply::json(&Value::Array(responses))))
    } else {
        // single API call
        let resp = rpc_request_handler(data, service, registry).await;
        Ok(Box::new(warp::reply::json(&resp)))
    }
}

/// Handler of single RPC request
/// Performs validation and executes corresponding rpc handler
async fn rpc_request_handler(
    req: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Value {
    let request: Map<String, Value>;
    let mut response = Map::new();

    // set defaults: protocol version to 2.0, request id to null
    response.insert("jsonrpc".to_string(), Value::String("2.0".to_string()));
    response.insert("id".to_string(), Value::Null);

    match req {
        Value::Object(data) => {
            request = data;
        }
        _ => {
            response.insert(
                "error".to_string(),
                JsonRpcError::invalid_request().serialize(),
            );
            return Value::Object(response);
        }
    }

    // parse request id
    match parse_request_id(&request) {
        Ok(request_id) => {
            response.insert("id".to_string(), request_id);
        }
        Err(err) => {
            response.insert("error".to_string(), err.serialize());
            return Value::Object(response);
        }
    };

    // verify protocol version
    if let Err(err) = verify_protocol(&request) {
        response.insert("error".to_string(), err.serialize());
        return Value::Object(response);
    }

    // parse parameters
    let params;
    match request.get("params") {
        Some(Value::Array(parameters)) => {
            params = parameters.to_vec();
        }
        _ => {
            response.insert(
                "error".to_string(),
                JsonRpcError::invalid_params().serialize(),
            );
            return Value::Object(response);
        }
    }

    // get rpc handler
    match request.get("method") {
        Some(Value::String(name)) => match registry.get(name) {
            Some(handler) => match handler(service, params).await {
                Ok(result) => {
                    response.insert("result".to_string(), result);
                }
                Err(err) => {
                    response.insert(
                        "error".to_string(),
                        JsonRpcError::internal_error(err.to_string()).serialize(),
                    );
                }
            },
            None => {
                response.insert(
                    "error".to_string(),
                    JsonRpcError::method_not_found().serialize(),
                );
            }
        },
        _ => {
            response.insert(
                "error".to_string(),
                JsonRpcError::invalid_request().serialize(),
            );
        }
    }

    Value::Object(response)
}

#[derive(Serialize)]
struct JsonRpcError {
    code: i16,
    message: String,
}

impl JsonRpcError {
    fn serialize(self) -> Value {
        serde_json::to_value(self).unwrap_or(Value::Null)
    }

    fn invalid_request() -> Self {
        Self {
            code: -32600,
            message: "Invalid Request".to_string(),
        }
    }

    fn invalid_params() -> Self {
        Self {
            code: -32602,
            message: "Invalid params".to_string(),
        }
    }

    fn method_not_found() -> Self {
        Self {
            code: -32601,
            message: "Method not found".to_string(),
        }
    }

    fn internal_error(message: String) -> Self {
        Self {
            code: -32000,
            message: format!("Server error: {}", message),
        }
    }
}

fn parse_request_id(request: &Map<String, Value>) -> Result<Value, JsonRpcError> {
    match request.get("id") {
        Some(req_id) => {
            if req_id.is_string() || req_id.is_number() || req_id.is_null() {
                Ok(req_id.clone())
            } else {
                Err(JsonRpcError::invalid_request())
            }
        }
        None => Ok(Value::Null),
    }
}

fn verify_protocol(request: &Map<String, Value>) -> Result<(), JsonRpcError> {
    if let Some(Value::String(protocol)) = request.get("jsonrpc") {
        if protocol == "2.0" {
            return Ok(());
        }
    }
    Err(JsonRpcError::invalid_request())
}
<|endoftext|>