<commit_before>use compositor::{Server, Shell, View};
use std::rc::Rc;
use std::time::Duration;
use wlroots::events::seat_events::SetCursorEvent;
use wlroots::pointer_events::ButtonEvent;
use wlroots::utils::current_time;
use wlroots::{CompositorHandle, Cursor, Origin, SeatHandle, SeatHandler, SurfaceHandle,
              XCursorManager};

#[derive(Debug, Default)]
pub struct SeatManager;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Action {
    /// We are moving a view.
    ///
    /// The start is the surface level coordinates of where the first click was
    Moving { start: Origin }
}

#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Seat {
    pub seat: SeatHandle,
    pub focused: Option<Rc<View>>,
    pub action: Option<Action>,
    pub has_client_cursor: bool,
    pub meta: bool
}

impl Seat {
    pub fn new(seat: SeatHandle) -> Seat {
        Seat { seat,
               meta: false,
               ..Seat::default() }
    }

    pub fn clear_focus(&mut self) {
        if let Some(focused_view) = self.focused.take() {
            focused_view.activate(false);
        }
        with_handles!([(seat: {&mut self.seat})] => {
            seat.keyboard_clear_focus();
        }).unwrap();
    }

    pub fn focus_view(&mut self, view: Rc<View>, views: &mut Vec<Rc<View>>) {
        if let Some(ref focused) = self.focused {
            if *focused == view {
                return
            }
            focused.activate(false);
        }
        self.focused = Some(view.clone());
        view.activate(true);

        if let Some(idx) = views.iter().position(|v| *v == view) {
            let v = views.remove(idx);
            views.insert(0, v);
        }

        with_handles!([(seat: {&mut self.seat})] => {
            if let Some(keyboard) = seat.get_keyboard() {
                with_handles!([(keyboard: {keyboard}), (surface: {view.surface()})] => {
                    seat.keyboard_notify_enter(surface,
                                               &mut keyboard.keycodes(),
                                               &mut keyboard.get_modifier_masks());
                }).unwrap();
            }
        }).unwrap();
    }

    pub fn send_button(&self, event: &ButtonEvent) {
        with_handles!([(seat: {&self.seat})] => {
            seat.pointer_notify_button(Duration::from_millis(event.time_msec() as _),
            event.button(),
            event.state() as u32);
        }).unwrap();
    }

    pub fn move_view<O>(&mut self, cursor: &mut Cursor, view: &View, start: O)
        where O: Into<Option<Origin>>
    {
        let Origin { x: shell_x,
                     y: shell_y } = view.origin.get();
        let (lx, ly) = cursor.coords();
        match start.into() {
            None => {
                let (view_sx, view_sy) = (lx - shell_x as f64, ly - shell_y as f64);
                let start = Origin::new(view_sx as _, view_sy as _);
                self.action = Some(Action::Moving { start });
            }
            Some(start) => {
                let pos = Origin::new(lx as i32 - start.x, ly as i32 - start.y);
                view.origin.replace(pos);
            }
        };
    }

    pub fn view_at_pointer(views: &mut [Rc<View>],
                           cursor: &mut Cursor)
                           -> (Option<Rc<View>>, Option<SurfaceHandle>, f64, f64) {
        for view in views {
            match view.shell {
                Shell::XdgV6(ref shell) => {
                    let (mut sx, mut sy) = (0.0, 0.0);
                    let surface = with_handles!([(shell: {shell})] => {
                        let (lx, ly) = cursor.coords();
                        let Origin {x: shell_x, y: shell_y} = view.origin.get();
                        let (view_sx, view_sy) = (lx - shell_x as f64, ly - shell_y as f64);
                        shell.surface_at(view_sx, view_sy, &mut sx, &mut sy)
                    }).unwrap();
                    if surface.is_some() {
                        return (Some(view.clone()), surface, sx, sy)
                    }
                }
            }
        }
        (None, None, 0.0, 0.0)
    }

    pub fn update_cursor_position(&mut self,
                                  cursor: &mut Cursor,
                                  xcursor_manager: &mut XCursorManager,
                                  views: &mut [Rc<View>],
                                  time_msec: Option<u32>) {
        let time = if let Some(time_msec) = time_msec {
            Duration::from_millis(time_msec as u64)
        } else {
            current_time()
        };

        match self.action {
            Some(Action::Moving { start }) => {
                self.focused = self.focused.take().map(|f| {
                                                           self.move_view(cursor, &f, start);
                                                           f
                                                       });
            }
            _ => {
                let (_view, surface, sx, sy) = Seat::view_at_pointer(views, cursor);
                match surface {
                    Some(surface) => {
                        with_handles!([(surface: {surface}), (seat: {&mut self.seat})] => {
                            seat.pointer_notify_enter(surface, sx, sy);
                            seat.pointer_notify_motion(time, sx, sy);
                        }).unwrap();
                    }
                    None => {
                        if self.has_client_cursor {
                            xcursor_manager.set_cursor_image("left_ptr".to_string(), cursor);
                            self.has_client_cursor = false;
                        }
                        with_handles!([(seat: {&mut self.seat})] => {
                            seat.pointer_clear_focus();
                        }).unwrap();
                    }
                }
            }
        }
    }
}

impl SeatHandler for SeatManager {
    fn cursor_set(&mut self, compositor: CompositorHandle, _: SeatHandle, event: &SetCursorEvent) {
        if let Some(surface) = event.surface() {
            with_handles!([(compositor: {compositor}), (surface: {surface})] => {
                let server: &mut Server = compositor.into();
                let Server { ref mut cursor,
                             ref mut seat,
                .. } = *server;
                with_handles!([(cursor: {&mut *cursor})] => {
                    let (hotspot_x, hotspot_y) = event.location();
                    let surface = &*surface;
                    cursor.set_surface(Some(surface), hotspot_x, hotspot_y);
                    seat.has_client_cursor = true;
                }).unwrap();
            }).unwrap();
        }
    }
}

impl SeatManager {
    pub fn new() -> Self {
        SeatManager::default()
    }
}
<commit_msg>set up drag icon listeners<commit_after>use compositor::{Server, Shell, View};
use std::rc::Rc;
use std::time::Duration;
use wlroots::events::seat_events::SetCursorEvent;
use wlroots::pointer_events::ButtonEvent;
use wlroots::utils::{current_time, L_DEBUG};
use wlroots::{CompositorHandle, Cursor, Origin, SeatHandle, SeatHandler, SurfaceHandle,
              XCursorManager, SurfaceHandler, DragIconHandler, DragIconHandle};

#[derive(Debug, Default)]
pub struct SeatManager;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Action {
    /// We are moving a view.
    ///
    /// The start is the surface level coordinates of where the first click was
    Moving { start: Origin }
}

#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Seat {
    pub seat: SeatHandle,
    pub focused: Option<Rc<View>>,
    pub action: Option<Action>,
    pub has_client_cursor: bool,
    pub meta: bool
}

impl Seat {
    pub fn new(seat: SeatHandle) -> Seat {
        Seat { seat,
               meta: false,
               ..Seat::default() }
    }

    pub fn clear_focus(&mut self) {
        if let Some(focused_view) = self.focused.take() {
            focused_view.activate(false);
        }
        with_handles!([(seat: {&mut self.seat})] => {
            seat.keyboard_clear_focus();
        }).unwrap();
    }

    pub fn focus_view(&mut self, view: Rc<View>, views: &mut Vec<Rc<View>>) {
        if let Some(ref focused) = self.focused {
            if *focused == view {
                return
            }
            focused.activate(false);
        }
        self.focused = Some(view.clone());
        view.activate(true);

        if let Some(idx) = views.iter().position(|v| *v == view) {
            let v = views.remove(idx);
            views.insert(0, v);
        }

        with_handles!([(seat: {&mut self.seat})] => {
            if let Some(keyboard) = seat.get_keyboard() {
                with_handles!([(keyboard: {keyboard}), (surface: {view.surface()})] => {
                    seat.keyboard_notify_enter(surface,
                                               &mut keyboard.keycodes(),
                                               &mut keyboard.get_modifier_masks());
                }).unwrap();
            }
        }).unwrap();
    }

    pub fn send_button(&self, event: &ButtonEvent) {
        with_handles!([(seat: {&self.seat})] => {
            seat.pointer_notify_button(Duration::from_millis(event.time_msec() as _),
            event.button(),
            event.state() as u32);
        }).unwrap();
    }

    pub fn move_view<O>(&mut self, cursor: &mut Cursor, view: &View, start: O)
        where O: Into<Option<Origin>>
    {
        let Origin { x: shell_x,
                     y: shell_y } = view.origin.get();
        let (lx, ly) = cursor.coords();
        match start.into() {
            None => {
                let (view_sx, view_sy) = (lx - shell_x as f64, ly - shell_y as f64);
                let start = Origin::new(view_sx as _, view_sy as _);
                self.action = Some(Action::Moving { start });
            }
            Some(start) => {
                let pos = Origin::new(lx as i32 - start.x, ly as i32 - start.y);
                view.origin.replace(pos);
            }
        };
    }

    pub fn view_at_pointer(views: &mut [Rc<View>],
                           cursor: &mut Cursor)
                           -> (Option<Rc<View>>, Option<SurfaceHandle>, f64, f64) {
        for view in views {
            match view.shell {
                Shell::XdgV6(ref shell) => {
                    let (mut sx, mut sy) = (0.0, 0.0);
                    let surface = with_handles!([(shell: {shell})] => {
                        let (lx, ly) = cursor.coords();
                        let Origin {x: shell_x, y: shell_y} = view.origin.get();
                        let (view_sx, view_sy) = (lx - shell_x as f64, ly - shell_y as f64);
                        shell.surface_at(view_sx, view_sy, &mut sx, &mut sy)
                    }).unwrap();
                    if surface.is_some() {
                        return (Some(view.clone()), surface, sx, sy)
                    }
                }
            }
        }
        (None, None, 0.0, 0.0)
    }

    pub fn update_cursor_position(&mut self,
                                  cursor: &mut Cursor,
                                  xcursor_manager: &mut XCursorManager,
                                  views: &mut [Rc<View>],
                                  time_msec: Option<u32>) {
        let time = if let Some(time_msec) = time_msec {
            Duration::from_millis(time_msec as u64)
        } else {
            current_time()
        };

        match self.action {
            Some(Action::Moving { start }) => {
                self.focused = self.focused.take().map(|f| {
                                                           self.move_view(cursor, &f, start);
                                                           f
                                                       });
            }
            _ => {
                let (_view, surface, sx, sy) = Seat::view_at_pointer(views, cursor);
                match surface {
                    Some(surface) => {
                        with_handles!([(surface: {surface}), (seat: {&mut self.seat})] => {
                            seat.pointer_notify_enter(surface, sx, sy);
                            seat.pointer_notify_motion(time, sx, sy);
                        }).unwrap();
                    }
                    None => {
                        if self.has_client_cursor {
                            xcursor_manager.set_cursor_image("left_ptr".to_string(), cursor);
                            self.has_client_cursor = false;
                        }
                        with_handles!([(seat: {&mut self.seat})] => {
                            seat.pointer_clear_focus();
                        }).unwrap();
                    }
                }
            }
        }
    }
}

struct WCDragIconHandler;

impl DragIconHandler for WCDragIconHandler {
    fn on_map(&mut self, compositor: CompositorHandle, drag_icon: DragIconHandle) {
        wlr_log!(L_DEBUG, "TODO: handle drag icon mapped");
    }

    fn on_unmap(&mut self, compositor: CompositorHandle, drag_icon: DragIconHandle) {
        wlr_log!(L_DEBUG, "TODO: handle drag icon unmapped");
    }

    fn destroyed(&mut self, compositor: CompositorHandle, drag_icon: DragIconHandle) {
        wlr_log!(L_DEBUG, "TODO: handle drag icon destroyed");
    }
}

impl SeatHandler for SeatManager {
    fn cursor_set(&mut self, compositor: CompositorHandle, _: SeatHandle, event: &SetCursorEvent) {
        if let Some(surface) = event.surface() {
            with_handles!([(compositor: {compositor}), (surface: {surface})] => {
                let server: &mut Server = compositor.into();
                let Server { ref mut cursor,
                             ref mut seat,
                .. } = *server;
                with_handles!([(cursor: {&mut *cursor})] => {
                    let (hotspot_x, hotspot_y) = event.location();
                    let surface = &*surface;
                    cursor.set_surface(Some(surface), hotspot_x, hotspot_y);
                    seat.has_client_cursor = true;
                }).unwrap();
            }).unwrap();
        }
    }

    fn new_drag_icon(&mut self,
                     compositor: CompositorHandle,
                     seat: SeatHandle,
                     drag_icon: DragIconHandle)
                     -> (Option<Box<DragIconHandler>>, Option<Box<SurfaceHandler>>) {
        (Some(Box::new(WCDragIconHandler)), None)
    }
}

impl SeatManager {
    pub fn new() -> Self {
        SeatManager::default()
    }
}
<|endoftext|>


<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::{CardEntry, CardTable};
use gc::swiper::CARD_SIZE;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::{in_kilo, on_different_cards, start_of_card};
use gc::swiper::old::OldGen;
use gc::swiper::Region;
use gc::swiper::young::YoungGen;
use mem;
use object::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,
    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,
        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,
            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 mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        let init_size = self.heap_size();

        self.mark_live();
        self.compute_forward();
        self.update_references();
        self.relocate();

        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)",
                in_ms(dur),
                in_kilo(init_size),
                in_kilo(new_size),
                in_kilo(garbage),
                garbage_ratio
            );
        });
    }

    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) {
                let mut young_refs = false;

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

                    if !field_addr.is_null() && !full.perm_space.contains(field_addr) {
                        let fwd_addr = full.fwd_table.forward_address(field_addr);
                        field.set(fwd_addr.to_mut_ptr());

                        if full.young.contains(fwd_addr) {
                            young_refs = true;
                        }
                    }
                });

                if young_refs {
                    full.fwd_table.set_young_refs(address);
                }
            }
        });

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

            if !root_ptr.is_null() && !self.perm_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 young_refs = false;
        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);

                if !young_refs {
                    young_refs = full.fwd_table.has_young_refs(address);
                }

                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());
                    full.update_card(dest, &mut young_refs);
                }

                last_dest = next_dest;
            }
        });

        if !last_dest.is_null() && !start_of_card(last_dest) && self.old.contains(last_dest) {
            self.update_card(last_dest, &mut young_refs);
        }

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

        assert!(self.fwd_end == self.old.total.end);
        assert!(self.old_top.is_null());
        self.old_top = self.fwd;

        let young = self.young.to_space();
        self.fwd = young.start.offset(object_size);
        self.fwd_end = young.end;

        young.start
    }

    fn update_crossing(&mut self, _last: Address, addr: Address, _array_ref: bool) {
        let offset = addr.to_usize() & (CARD_SIZE - 1);
        let offset_words = offset / mem::ptr_width_usize();

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

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

    fn update_card(&mut self, addr: Address, young_refs: &mut bool) {
        let card = self.old.card_from_address(addr);

        let card_entry = if *young_refs {
            CardEntry::Dirty
        } else {
            CardEntry::Clean
        };
        self.card_table.set(card, card_entry);

        *young_refs = false;
    }

    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, AddressWithYoungRefs>,
}

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, AddressWithYoungRefs::new(fwd));
    }

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

    fn has_young_refs(&mut self, addr: Address) -> bool {
        self.data
            .get(&addr)
            .expect("no forward address found.")
            .has_young_refs()
    }

    fn set_young_refs(&mut self, addr: Address) {
        let value = self.data.get_mut(&addr).expect("no forward address found.");
        *value = value.set_young_refs();
    }
}

#[derive(Copy, Clone)]
struct AddressWithYoungRefs {
    data: usize,
}

impl AddressWithYoungRefs {
    fn new(addr: Address) -> AddressWithYoungRefs {
        AddressWithYoungRefs {
            data: addr.to_usize(),
        }
    }

    fn address(self) -> Address {
        (self.data & !1).into()
    }

    fn has_young_refs(self) -> bool {
        (self.data & 1) != 0
    }

    fn set_young_refs(self) -> AddressWithYoungRefs {
        AddressWithYoungRefs {
            data: self.data | 1,
        }
    }
}
<commit_msg>swiper: set correct crossing for full GC<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::{CardEntry, CardTable};
use gc::swiper::CARD_SIZE;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::{in_kilo, on_different_cards, start_of_card};
use gc::swiper::old::OldGen;
use gc::swiper::Region;
use gc::swiper::young::YoungGen;
use mem;
use object::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,
    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,
        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,
            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 mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        let init_size = self.heap_size();

        self.mark_live();
        self.compute_forward();
        self.update_references();
        self.relocate();

        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)",
                in_ms(dur),
                in_kilo(init_size),
                in_kilo(new_size),
                in_kilo(garbage),
                garbage_ratio
            );
        });
    }

    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) {
                let mut young_refs = false;

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

                    if !field_addr.is_null() && !full.perm_space.contains(field_addr) {
                        let fwd_addr = full.fwd_table.forward_address(field_addr);
                        field.set(fwd_addr.to_mut_ptr());

                        if full.young.contains(fwd_addr) {
                            young_refs = true;
                        }
                    }
                });

                if young_refs {
                    full.fwd_table.set_young_refs(address);
                }
            }
        });

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

            if !root_ptr.is_null() && !self.perm_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 young_refs = false;
        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);

                if !young_refs {
                    young_refs = full.fwd_table.has_young_refs(address);
                }

                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());
                    full.update_card(dest, &mut young_refs);
                }

                last_dest = next_dest;
            }
        });

        if !last_dest.is_null() && !start_of_card(last_dest) && self.old.contains(last_dest) {
            self.update_card(last_dest, &mut young_refs);
        }

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

        assert!(self.fwd_end == self.old.total.end);
        assert!(self.old_top.is_null());
        self.old_top = self.fwd;

        let young = self.young.to_space();
        self.fwd = young.start.offset(object_size);
        self.fwd_end = young.end;

        young.start
    }

    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(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()) as u8;

            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 update_card(&mut self, addr: Address, young_refs: &mut bool) {
        let card = self.old.card_from_address(addr);

        let card_entry = if *young_refs {
            CardEntry::Dirty
        } else {
            CardEntry::Clean
        };
        self.card_table.set(card, card_entry);

        *young_refs = false;
    }

    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, AddressWithYoungRefs>,
}

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, AddressWithYoungRefs::new(fwd));
    }

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

    fn has_young_refs(&mut self, addr: Address) -> bool {
        self.data
            .get(&addr)
            .expect("no forward address found.")
            .has_young_refs()
    }

    fn set_young_refs(&mut self, addr: Address) {
        let value = self.data.get_mut(&addr).expect("no forward address found.");
        *value = value.set_young_refs();
    }
}

#[derive(Copy, Clone)]
struct AddressWithYoungRefs {
    data: usize,
}

impl AddressWithYoungRefs {
    fn new(addr: Address) -> AddressWithYoungRefs {
        AddressWithYoungRefs {
            data: addr.to_usize(),
        }
    }

    fn address(self) -> Address {
        (self.data & !1).into()
    }

    fn has_young_refs(self) -> bool {
        (self.data & 1) != 0
    }

    fn set_young_refs(self) -> AddressWithYoungRefs {
        AddressWithYoungRefs {
            data: self.data | 1,
        }
    }
}
<|endoftext|>


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

use builder::commands as cmd;
use quire::validate as V;
use serde::de::{self, Deserializer, Deserialize};
use serde::de::{VariantAccess, Visitor, EnumAccess};

use build_step::{Step, BuildStep};

const COMMANDS: &'static [&'static str] = &[
    "Alpine",
    "AlpineRepo",
    "Ubuntu",
    "UbuntuRepo",
    "UbuntuRelease",
    "UbuntuPPA",
    "UbuntuUniverse",
    "AptTrust",
    "Repo",
    "Install",
    "BuildDeps",
    "Git",
    "GitInstall",
    "GitDescribe",
    "PipConfig",
    "Py2Install",
    "Py2Requirements",
    "Py3Install",
    "Py3Requirements",
    "Tar",
    "TarInstall",
    "Unzip",
    "Sh",
    "Cmd",
    "RunAs",
    "Env",
    "Text",
    "Copy",
    "Download",
    "EnsureDir",
    "CacheDirs",
    "EmptyDir",
    "Remove",
    "Depends",
    "Container",
    "Build",
    "SubConfig",
    "NpmConfig",
    "NpmDependencies",
    "YarnDependencies",
    "NpmInstall",
    "GemInstall",
    "GemBundle",
    "GemConfig",
    "ComposerInstall",
    "ComposerDependencies",
    "ComposerConfig",
];

pub enum CommandName {
    Alpine,
    AlpineRepo,
    Ubuntu,
    UbuntuRepo,
    UbuntuRelease,
    UbuntuPPA,
    UbuntuUniverse,
    AptTrust,
    Repo,
    Install,
    BuildDeps,
    Git,
    GitInstall,
    GitDescribe,
    PipConfig,
    Py2Install,
    Py2Requirements,
    Py3Install,
    Py3Requirements,
    Tar,
    TarInstall,
    Unzip,
    Sh,
    Cmd,
    RunAs,
    Env,
    Text,
    Copy,
    Download,
    EnsureDir,
    CacheDirs,
    EmptyDir,
    Remove,
    Depends,
    Container,
    Build,
    SubConfig,
    NpmConfig,
    NpmDependencies,
    YarnDependencies,
    NpmInstall,
    GemInstall,
    GemBundle,
    GemConfig,
    ComposerInstall,
    ComposerDependencies,
    ComposerConfig,
}

pub struct NameVisitor;
pub struct StepVisitor;

#[cfg(not(feature="containers"))]
pub fn builder_validator<'x>() -> V::Enum<'x> {
    // TODO(tailhook) temporarily, until we support all commands here
    V::Enum::new()
    .option("Ubuntu", cmd::ubuntu::Ubuntu::config())
    .option("UbuntuRelease", cmd::ubuntu::UbuntuRelease::config())
    .option("UbuntuRepo", cmd::ubuntu::UbuntuRepo::config())
    .option("UbuntuPPA", cmd::ubuntu::UbuntuPPA::config())
    .option("UbuntuUniverse", cmd::ubuntu::UbuntuUniverse::config())
    .option("AptTrust", cmd::ubuntu::AptTrust::config())
    .option("Download", cmd::download::Download::config())
}

#[cfg(feature="containers")]
pub fn builder_validator<'x>() -> V::Enum<'x> {
    V::Enum::new()
    .option("Alpine", cmd::alpine::Alpine::config())
    .option("AlpineRepo", cmd::alpine::AlpineRepo::config())
    .option("Ubuntu", cmd::ubuntu::Ubuntu::config())
    .option("UbuntuRelease", cmd::ubuntu::UbuntuRelease::config())
    .option("UbuntuRepo", cmd::ubuntu::UbuntuRepo::config())
    .option("UbuntuPPA", cmd::ubuntu::UbuntuPPA::config())
    .option("UbuntuUniverse", cmd::ubuntu::UbuntuUniverse::config())
    .option("AptTrust", cmd::ubuntu::AptTrust::config())
    .option("Repo", cmd::packaging::Repo::config())
    .option("Install", cmd::packaging::Install::config())
    .option("BuildDeps", cmd::packaging::BuildDeps::config())
    .option("Container", cmd::subcontainer::Container::config())
    .option("SubConfig", cmd::subcontainer::SubConfig::config())
    .option("Build", cmd::subcontainer::Build::config())
    .option("Text", cmd::text::Text::config())
    .option("Copy", cmd::copy::Copy::config())

    .option("Sh", cmd::generic::Sh::config())
    .option("Cmd", cmd::generic::Cmd::config())
    .option("RunAs", cmd::generic::RunAs::config())
    .option("Remove", cmd::dirs::Remove::config())
    .option("EnsureDir", cmd::dirs::EnsureDir::config())
    .option("EmptyDir", cmd::dirs::EmptyDir::config())
    .option("CacheDirs", cmd::dirs::CacheDirs::config())
    .option("Env", cmd::generic::Env::config())
    .option("Depends", cmd::copy::Depends::config())
    .option("Git", cmd::vcs::Git::config())
    .option("GitInstall", cmd::vcs::GitInstall::config())
    .option("GitDescribe", cmd::vcs::GitDescribe::config())
    .option("Tar", cmd::tarcmd::Tar::config())
    .option("TarInstall", cmd::tarcmd::TarInstall::config())
    .option("Unzip", cmd::unzip::Unzip::config())
    .option("Download", cmd::download::Download::config())

    // Python
    .option("PipConfig", cmd::pip::PipConfig::config())
    .option("Py2Install", cmd::pip::Py2Install::config())
    .option("Py2Requirements", cmd::pip::Py2Requirements::config())
    .option("Py3Install", cmd::pip::Py3Install::config())
    .option("Py3Requirements", cmd::pip::Py3Requirements::config())

    // Node.js
    .option("NpmConfig", cmd::npm::NpmConfig::config())
    .option("NpmInstall", cmd::npm::NpmInstall::config())
    .option("NpmDependencies", cmd::npm::NpmDependencies::config())
    .option("YarnDependencies", cmd::npm::YarnDependencies::config())

    // Composer
    .option("ComposerConfig", cmd::composer::ComposerConfig::config())
    .option("ComposerInstall", cmd::composer::ComposerInstall::config())
    .option("ComposerDependencies",
        cmd::composer::ComposerDependencies::config())

    // Ruby
    .option("GemConfig", cmd::gem::GemConfig::config())
    .option("GemInstall", cmd::gem::GemInstall::config())
    .option("GemBundle", cmd::gem::GemBundle::config())
}

impl<'a> Visitor<'a> for NameVisitor {
    type Value = CommandName;
    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "build step is one of {}", COMMANDS.join(", "))
    }
    fn visit_str<E: de::Error>(self, val: &str) -> Result<CommandName, E> {
        use self::CommandName::*;
        let res = match val {
            "Alpine" => Alpine,
            "AlpineRepo" => AlpineRepo,
            "Ubuntu" => Ubuntu,
            "UbuntuRepo" => UbuntuRepo,
            "UbuntuRelease" => UbuntuRelease,
            "UbuntuPPA" => UbuntuPPA,
            "UbuntuUniverse" => UbuntuUniverse,
            "AptTrust" => AptTrust,
            "Repo" => Repo,
            "Install" => Install,
            "BuildDeps" => BuildDeps,
            "Git" => Git,
            "GitInstall" => GitInstall,
            "GitDescribe" => GitDescribe,
            "PipConfig" => PipConfig,
            "Py2Install" => Py2Install,
            "Py2Requirements" => Py2Requirements,
            "Py3Install" => Py3Install,
            "Py3Requirements" => Py3Requirements,
            "Tar" => Tar,
            "TarInstall" => TarInstall,
            "Unzip" => Unzip,
            "Sh" => Sh,
            "Cmd" => Cmd,
            "RunAs" => RunAs,
            "Env" => Env,
            "Text" => Text,
            "Copy" => Copy,
            "Download" => Download,
            "EnsureDir" => EnsureDir,
            "CacheDirs" => CacheDirs,
            "EmptyDir" => EmptyDir,
            "Remove" => Remove,
            "Depends" => Depends,
            "Container" => Container,
            "Build" => Build,
            "SubConfig" => SubConfig,
            "NpmConfig" => NpmConfig,
            "NpmDependencies" => NpmDependencies,
            "YarnDependencies" => YarnDependencies,
            "NpmInstall" => NpmInstall,
            "GemInstall" => GemInstall,
            "GemBundle" => GemBundle,
            "GemConfig" => GemConfig,
            "ComposerInstall" => ComposerInstall,
            "ComposerDependencies" => ComposerDependencies,
            "ComposerConfig" => ComposerConfig,
            _ => return Err(E::custom("invalid build step")),
        };
        Ok(res)
    }
}

fn decode<'x, T, V>(v: V)
    -> Result<Step, V::Error>
    where
        T: BuildStep + Deserialize<'x> + 'static,
        V: VariantAccess<'x>,
{
    v.newtype_variant::<T>().map(|x| Step(Rc::new(x) as Rc<BuildStep>))
}

impl<'a> Visitor<'a> for StepVisitor {
    type Value = Step;
    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "build step is one of {}", COMMANDS.join(", "))
    }
    #[cfg(not(feature="containers"))]
    fn visit_enum<A>(self, data: A) -> Result<Step, A::Error>
        where A: EnumAccess<'a>,
    {
        use self::CommandName::*;
        let (tag, v): (CommandName, _) = data.variant()?;
        match tag {
            Ubuntu => decode::<cmd::ubuntu::Ubuntu, _>(v),
            UbuntuRepo => decode::<cmd::ubuntu::UbuntuRepo, _>(v),
            UbuntuRelease => decode::<cmd::ubuntu::UbuntuRelease, _>(v),
            UbuntuPPA => decode::<cmd::ubuntu::UbuntuPPA, _>(v),
            UbuntuUniverse => decode::<cmd::ubuntu::UbuntuUniverse, _>(v),
            Download => decode::<cmd::download::Download, _>(v),
            // TODO(tailhook) temporarily, until we support all commands here
            _ => unimplemented!(),
        }
    }

    #[cfg(feature="containers")]
    fn visit_enum<A>(self, data: A) -> Result<Step, A::Error>
        where A: EnumAccess<'a>,
    {
        use self::CommandName::*;
        let (tag, v) = data.variant()?;
        match tag {
            Alpine => decode::<cmd::alpine::Alpine, _>(v),
            AlpineRepo => decode::<cmd::alpine::AlpineRepo, _>(v),
            Ubuntu => decode::<cmd::ubuntu::Ubuntu, _>(v),
            UbuntuRepo => decode::<cmd::ubuntu::UbuntuRepo, _>(v),
            UbuntuRelease => decode::<cmd::ubuntu::UbuntuRelease, _>(v),
            UbuntuPPA => decode::<cmd::ubuntu::UbuntuPPA, _>(v),
            UbuntuUniverse => decode::<cmd::ubuntu::UbuntuUniverse, _>(v),
            AptTrust => decode::<cmd::ubuntu::AptTrust, _>(v),
            Repo => decode::<cmd::packaging::Repo, _>(v),
            Install => decode::<cmd::packaging::Install, _>(v),
            BuildDeps => decode::<cmd::packaging::BuildDeps, _>(v),
            Git => decode::<cmd::vcs::Git, _>(v),
            GitInstall => decode::<cmd::vcs::GitInstall, _>(v),
            GitDescribe => decode::<cmd::vcs::GitDescribe, _>(v),
            PipConfig => decode::<cmd::pip::PipConfig, _>(v),
            Py2Install => decode::<cmd::pip::Py2Install, _>(v),
            Py2Requirements => decode::<cmd::pip::Py2Requirements, _>(v),
            Py3Install => decode::<cmd::pip::Py3Install, _>(v),
            Py3Requirements => decode::<cmd::pip::Py3Requirements, _>(v),
            Tar => decode::<cmd::tarcmd::Tar, _>(v),
            TarInstall => decode::<cmd::tarcmd::TarInstall, _>(v),
            Unzip => decode::<cmd::unzip::Unzip, _>(v),
            Sh => decode::<cmd::generic::Sh, _>(v),
            Cmd => decode::<cmd::generic::Cmd, _>(v),
            RunAs => decode::<cmd::generic::RunAs, _>(v),
            Env => decode::<cmd::generic::Env, _>(v),
            Text => decode::<cmd::text::Text, _>(v),
            Copy => decode::<cmd::copy::Copy, _>(v),
            Download => decode::<cmd::download::Download, _>(v),
            EnsureDir => decode::<cmd::dirs::EnsureDir, _>(v),
            CacheDirs => decode::<cmd::dirs::CacheDirs, _>(v),
            EmptyDir => decode::<cmd::dirs::EmptyDir, _>(v),
            Remove => decode::<cmd::dirs::Remove, _>(v),
            Depends => decode::<cmd::copy::Depends, _>(v),
            Container => decode::<cmd::subcontainer::Container, _>(v),
            Build => decode::<cmd::subcontainer::Build, _>(v),
            SubConfig => decode::<cmd::subcontainer::SubConfig, _>(v),
            NpmConfig => decode::<cmd::npm::NpmConfig, _>(v),
            NpmDependencies => decode::<cmd::npm::NpmDependencies, _>(v),
            YarnDependencies => decode::<cmd::npm::YarnDependencies, _>(v),
            NpmInstall => decode::<cmd::npm::NpmInstall, _>(v),
            GemInstall => decode::<cmd::gem::GemInstall, _>(v),
            GemBundle => decode::<cmd::gem::GemBundle, _>(v),
            GemConfig => decode::<cmd::gem::GemConfig, _>(v),
            ComposerInstall => decode::<cmd::composer::ComposerInstall, _>(v),
            ComposerDependencies
            => decode::<cmd::composer::ComposerDependencies, _>(v),
            ComposerConfig => decode::<cmd::composer::ComposerConfig, _>(v),
        }
    }
}

impl<'a> Deserialize<'a> for CommandName {
    fn deserialize<D: Deserializer<'a>>(d: D) -> Result<CommandName, D::Error>
    {
        d.deserialize_identifier(NameVisitor)
    }
}

impl<'a> Deserialize<'a> for Step {
    fn deserialize<D: Deserializer<'a>>(d: D) -> Result<Step, D::Error> {
        d.deserialize_enum("BuildStep", COMMANDS, StepVisitor)
    }
}
<commit_msg>Allow to parse whole configuration in non-containers mode<commit_after>use std::fmt;
use std::rc::Rc;

use builder::commands as cmd;
use quire::validate as V;
use serde::de::{self, Deserializer, Deserialize};
use serde::de::{VariantAccess, Visitor, EnumAccess};

use build_step::{Step, BuildStep};

const COMMANDS: &'static [&'static str] = &[
    "Alpine",
    "AlpineRepo",
    "Ubuntu",
    "UbuntuRepo",
    "UbuntuRelease",
    "UbuntuPPA",
    "UbuntuUniverse",
    "AptTrust",
    "Repo",
    "Install",
    "BuildDeps",
    "Git",
    "GitInstall",
    "GitDescribe",
    "PipConfig",
    "Py2Install",
    "Py2Requirements",
    "Py3Install",
    "Py3Requirements",
    "Tar",
    "TarInstall",
    "Unzip",
    "Sh",
    "Cmd",
    "RunAs",
    "Env",
    "Text",
    "Copy",
    "Download",
    "EnsureDir",
    "CacheDirs",
    "EmptyDir",
    "Remove",
    "Depends",
    "Container",
    "Build",
    "SubConfig",
    "NpmConfig",
    "NpmDependencies",
    "YarnDependencies",
    "NpmInstall",
    "GemInstall",
    "GemBundle",
    "GemConfig",
    "ComposerInstall",
    "ComposerDependencies",
    "ComposerConfig",
];

pub enum CommandName {
    Alpine,
    AlpineRepo,
    Ubuntu,
    UbuntuRepo,
    UbuntuRelease,
    UbuntuPPA,
    UbuntuUniverse,
    AptTrust,
    Repo,
    Install,
    BuildDeps,
    Git,
    GitInstall,
    GitDescribe,
    PipConfig,
    Py2Install,
    Py2Requirements,
    Py3Install,
    Py3Requirements,
    Tar,
    TarInstall,
    Unzip,
    Sh,
    Cmd,
    RunAs,
    Env,
    Text,
    Copy,
    Download,
    EnsureDir,
    CacheDirs,
    EmptyDir,
    Remove,
    Depends,
    Container,
    Build,
    SubConfig,
    NpmConfig,
    NpmDependencies,
    YarnDependencies,
    NpmInstall,
    GemInstall,
    GemBundle,
    GemConfig,
    ComposerInstall,
    ComposerDependencies,
    ComposerConfig,
}

pub struct NameVisitor;
pub struct StepVisitor;

pub fn builder_validator<'x>() -> V::Enum<'x> {
    V::Enum::new()
    .option("Alpine", cmd::alpine::Alpine::config())
    .option("AlpineRepo", cmd::alpine::AlpineRepo::config())
    .option("Ubuntu", cmd::ubuntu::Ubuntu::config())
    .option("UbuntuRelease", cmd::ubuntu::UbuntuRelease::config())
    .option("UbuntuRepo", cmd::ubuntu::UbuntuRepo::config())
    .option("UbuntuPPA", cmd::ubuntu::UbuntuPPA::config())
    .option("UbuntuUniverse", cmd::ubuntu::UbuntuUniverse::config())
    .option("AptTrust", cmd::ubuntu::AptTrust::config())
    .option("Repo", cmd::packaging::Repo::config())
    .option("Install", cmd::packaging::Install::config())
    .option("BuildDeps", cmd::packaging::BuildDeps::config())
    .option("Container", cmd::subcontainer::Container::config())
    .option("SubConfig", cmd::subcontainer::SubConfig::config())
    .option("Build", cmd::subcontainer::Build::config())
    .option("Text", cmd::text::Text::config())
    .option("Copy", cmd::copy::Copy::config())

    .option("Sh", cmd::generic::Sh::config())
    .option("Cmd", cmd::generic::Cmd::config())
    .option("RunAs", cmd::generic::RunAs::config())
    .option("Remove", cmd::dirs::Remove::config())
    .option("EnsureDir", cmd::dirs::EnsureDir::config())
    .option("EmptyDir", cmd::dirs::EmptyDir::config())
    .option("CacheDirs", cmd::dirs::CacheDirs::config())
    .option("Env", cmd::generic::Env::config())
    .option("Depends", cmd::copy::Depends::config())
    .option("Git", cmd::vcs::Git::config())
    .option("GitInstall", cmd::vcs::GitInstall::config())
    .option("GitDescribe", cmd::vcs::GitDescribe::config())
    .option("Tar", cmd::tarcmd::Tar::config())
    .option("TarInstall", cmd::tarcmd::TarInstall::config())
    .option("Unzip", cmd::unzip::Unzip::config())
    .option("Download", cmd::download::Download::config())

    // Python
    .option("PipConfig", cmd::pip::PipConfig::config())
    .option("Py2Install", cmd::pip::Py2Install::config())
    .option("Py2Requirements", cmd::pip::Py2Requirements::config())
    .option("Py3Install", cmd::pip::Py3Install::config())
    .option("Py3Requirements", cmd::pip::Py3Requirements::config())

    // Node.js
    .option("NpmConfig", cmd::npm::NpmConfig::config())
    .option("NpmInstall", cmd::npm::NpmInstall::config())
    .option("NpmDependencies", cmd::npm::NpmDependencies::config())
    .option("YarnDependencies", cmd::npm::YarnDependencies::config())

    // Composer
    .option("ComposerConfig", cmd::composer::ComposerConfig::config())
    .option("ComposerInstall", cmd::composer::ComposerInstall::config())
    .option("ComposerDependencies",
        cmd::composer::ComposerDependencies::config())

    // Ruby
    .option("GemConfig", cmd::gem::GemConfig::config())
    .option("GemInstall", cmd::gem::GemInstall::config())
    .option("GemBundle", cmd::gem::GemBundle::config())
}

impl<'a> Visitor<'a> for NameVisitor {
    type Value = CommandName;
    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "build step is one of {}", COMMANDS.join(", "))
    }
    fn visit_str<E: de::Error>(self, val: &str) -> Result<CommandName, E> {
        use self::CommandName::*;
        let res = match val {
            "Alpine" => Alpine,
            "AlpineRepo" => AlpineRepo,
            "Ubuntu" => Ubuntu,
            "UbuntuRepo" => UbuntuRepo,
            "UbuntuRelease" => UbuntuRelease,
            "UbuntuPPA" => UbuntuPPA,
            "UbuntuUniverse" => UbuntuUniverse,
            "AptTrust" => AptTrust,
            "Repo" => Repo,
            "Install" => Install,
            "BuildDeps" => BuildDeps,
            "Git" => Git,
            "GitInstall" => GitInstall,
            "GitDescribe" => GitDescribe,
            "PipConfig" => PipConfig,
            "Py2Install" => Py2Install,
            "Py2Requirements" => Py2Requirements,
            "Py3Install" => Py3Install,
            "Py3Requirements" => Py3Requirements,
            "Tar" => Tar,
            "TarInstall" => TarInstall,
            "Unzip" => Unzip,
            "Sh" => Sh,
            "Cmd" => Cmd,
            "RunAs" => RunAs,
            "Env" => Env,
            "Text" => Text,
            "Copy" => Copy,
            "Download" => Download,
            "EnsureDir" => EnsureDir,
            "CacheDirs" => CacheDirs,
            "EmptyDir" => EmptyDir,
            "Remove" => Remove,
            "Depends" => Depends,
            "Container" => Container,
            "Build" => Build,
            "SubConfig" => SubConfig,
            "NpmConfig" => NpmConfig,
            "NpmDependencies" => NpmDependencies,
            "YarnDependencies" => YarnDependencies,
            "NpmInstall" => NpmInstall,
            "GemInstall" => GemInstall,
            "GemBundle" => GemBundle,
            "GemConfig" => GemConfig,
            "ComposerInstall" => ComposerInstall,
            "ComposerDependencies" => ComposerDependencies,
            "ComposerConfig" => ComposerConfig,
            _ => return Err(E::custom("invalid build step")),
        };
        Ok(res)
    }
}

fn decode<'x, T, V>(v: V)
    -> Result<Step, V::Error>
    where
        T: BuildStep + Deserialize<'x> + 'static,
        V: VariantAccess<'x>,
{
    v.newtype_variant::<T>().map(|x| Step(Rc::new(x) as Rc<BuildStep>))
}

impl<'a> Visitor<'a> for StepVisitor {
    type Value = Step;
    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "build step is one of {}", COMMANDS.join(", "))
    }
    fn visit_enum<A>(self, data: A) -> Result<Step, A::Error>
        where A: EnumAccess<'a>,
    {
        use self::CommandName::*;
        let (tag, v) = data.variant()?;
        match tag {
            Alpine => decode::<cmd::alpine::Alpine, _>(v),
            AlpineRepo => decode::<cmd::alpine::AlpineRepo, _>(v),
            Ubuntu => decode::<cmd::ubuntu::Ubuntu, _>(v),
            UbuntuRepo => decode::<cmd::ubuntu::UbuntuRepo, _>(v),
            UbuntuRelease => decode::<cmd::ubuntu::UbuntuRelease, _>(v),
            UbuntuPPA => decode::<cmd::ubuntu::UbuntuPPA, _>(v),
            UbuntuUniverse => decode::<cmd::ubuntu::UbuntuUniverse, _>(v),
            AptTrust => decode::<cmd::ubuntu::AptTrust, _>(v),
            Repo => decode::<cmd::packaging::Repo, _>(v),
            Install => decode::<cmd::packaging::Install, _>(v),
            BuildDeps => decode::<cmd::packaging::BuildDeps, _>(v),
            Git => decode::<cmd::vcs::Git, _>(v),
            GitInstall => decode::<cmd::vcs::GitInstall, _>(v),
            GitDescribe => decode::<cmd::vcs::GitDescribe, _>(v),
            PipConfig => decode::<cmd::pip::PipConfig, _>(v),
            Py2Install => decode::<cmd::pip::Py2Install, _>(v),
            Py2Requirements => decode::<cmd::pip::Py2Requirements, _>(v),
            Py3Install => decode::<cmd::pip::Py3Install, _>(v),
            Py3Requirements => decode::<cmd::pip::Py3Requirements, _>(v),
            Tar => decode::<cmd::tarcmd::Tar, _>(v),
            TarInstall => decode::<cmd::tarcmd::TarInstall, _>(v),
            Unzip => decode::<cmd::unzip::Unzip, _>(v),
            Sh => decode::<cmd::generic::Sh, _>(v),
            Cmd => decode::<cmd::generic::Cmd, _>(v),
            RunAs => decode::<cmd::generic::RunAs, _>(v),
            Env => decode::<cmd::generic::Env, _>(v),
            Text => decode::<cmd::text::Text, _>(v),
            Copy => decode::<cmd::copy::Copy, _>(v),
            Download => decode::<cmd::download::Download, _>(v),
            EnsureDir => decode::<cmd::dirs::EnsureDir, _>(v),
            CacheDirs => decode::<cmd::dirs::CacheDirs, _>(v),
            EmptyDir => decode::<cmd::dirs::EmptyDir, _>(v),
            Remove => decode::<cmd::dirs::Remove, _>(v),
            Depends => decode::<cmd::copy::Depends, _>(v),
            Container => decode::<cmd::subcontainer::Container, _>(v),
            Build => decode::<cmd::subcontainer::Build, _>(v),
            SubConfig => decode::<cmd::subcontainer::SubConfig, _>(v),
            NpmConfig => decode::<cmd::npm::NpmConfig, _>(v),
            NpmDependencies => decode::<cmd::npm::NpmDependencies, _>(v),
            YarnDependencies => decode::<cmd::npm::YarnDependencies, _>(v),
            NpmInstall => decode::<cmd::npm::NpmInstall, _>(v),
            GemInstall => decode::<cmd::gem::GemInstall, _>(v),
            GemBundle => decode::<cmd::gem::GemBundle, _>(v),
            GemConfig => decode::<cmd::gem::GemConfig, _>(v),
            ComposerInstall => decode::<cmd::composer::ComposerInstall, _>(v),
            ComposerDependencies
            => decode::<cmd::composer::ComposerDependencies, _>(v),
            ComposerConfig => decode::<cmd::composer::ComposerConfig, _>(v),
        }
    }
}

impl<'a> Deserialize<'a> for CommandName {
    fn deserialize<D: Deserializer<'a>>(d: D) -> Result<CommandName, D::Error>
    {
        d.deserialize_identifier(NameVisitor)
    }
}

impl<'a> Deserialize<'a> for Step {
    fn deserialize<D: Deserializer<'a>>(d: D) -> Result<Step, D::Error> {
        d.deserialize_enum("BuildStep", COMMANDS, StepVisitor)
    }
}
<|endoftext|>


<commit_before>use super::{Edge, Graph, NodeId};

use std::collections::{BTreeSet, VecDeque};
use std::time::Instant;

type Component = BTreeSet<NodeId>;
impl<E: Edge> Graph<E> {
    pub fn count_components(&self) -> usize {
        let start = Instant::now();
        let mut visited = vec![false; self.node_info.len()];
        let mut components = Vec::<Component>::new();
        for id in 0..visited.len() {
            if !visited[id] {
                let mut comp = self.dfs(id, &mut visited);
                for component in &mut components {
                    if !component.is_disjoint(&comp) {
                        component.append(&mut comp);
                        break;
                    }
                }
                if !comp.is_empty() {
                    components.push(comp);
                }
            } else {
                continue;
            }
        }

        let end = Instant::now();
        println!("Counting Components took {:?}", end.duration_since(start));
        components.len()
    }

    fn dfs(&self, start: NodeId, visited: &mut Vec<bool>) -> Component {
        let mut result = Component::new();
        let mut queue = VecDeque::<NodeId>::new();
        queue.push_front(start);
        while let Some(n) = queue.pop_front() {
            result.insert(n);
            if !visited[n] {
                visited[n] = true;
                queue.extend(self.outgoing_edges_for(n)
                                 .iter()
                                 .map(|e| e.get_dest_id()));
            }
        }
        result
    }
}

#[test]
fn count() {
    use super::{EdgeInfo, NodeInfo};
    let g = Graph::new(vec![NodeInfo::new(1, 2.3, 3.4, 0),
                            NodeInfo::new(2, 2.3, 3.4, 0),
                            NodeInfo::new(3, 2.3, 3.4, 0),
                            NodeInfo::new(4, 2.3, 3.4, 0),
                            NodeInfo::new(5, 2.3, 3.4, 0)],
                       vec![EdgeInfo::new(0, 1, 3, 3),
                            EdgeInfo::new(0, 2, 3, 3),
                            EdgeInfo::new(2, 3, 3, 3),
                            EdgeInfo::new(4, 0, 3, 3)]);
    assert_eq!(g.count_components(), 1)
}
<commit_msg>Use union-find data structure for counting components<commit_after>use super::{Edge, Graph, NodeId};

use std::collections::{BTreeSet, VecDeque};
use std::time::Instant;

impl<E: Edge> Graph<E> {
    pub fn count_components(&self) -> usize {
        let start = Instant::now();
        let mut union = UnionFind::new(self.node_info.len());
        for id in 0..self.node_info.len() {
            if id == union.find(id) {
                self.dfs(id, &mut union);
            }
        }
        let count = union.count();
        let end = Instant::now();
        println!("Counting Components took {:?}", end.duration_since(start));
        count
    }

    fn dfs(&self, start: NodeId, union: &mut UnionFind) {
        let mut queue = Vec::<NodeId>::new();
        queue.push(start);
        while let Some(n) = queue.pop() {
            if union.find(n) == n {
                union.union(start, n);
                queue.extend(self.outgoing_edges_for(n)
                                 .iter()
                                 .map(|e| e.get_dest_id()));
            }
#[derive(Debug)]
struct UnionFind {
    parent: Vec<NodeId>,
}

impl UnionFind {
    pub fn new(size: usize) -> UnionFind {
        let parent = (0..size).collect();
        UnionFind { parent: parent }
    }

    pub fn find(&mut self, id: NodeId) -> NodeId {
        let mut visited_ids = vec![id];
        let mut cur_id = id;
        let mut par_id = self.parent[id];
        while cur_id != par_id {
            cur_id = par_id;
            visited_ids.push(cur_id);
            par_id = self.parent[cur_id];
        }
        for id in visited_ids {
            self.parent[id] = par_id;
        }
        par_id
    }

    pub fn union(&mut self, r: NodeId, s: NodeId) {
        let r_par = self.find(r);
        let s_par = self.find(s);
        if r_par != s_par {
            self.parent[s_par] = r_par;
        }
    }

    fn count(&self) -> usize {
        let mut result = 0;
        for (index, &par) in self.parent.iter().enumerate() {
            if index == par {
                result += 1;
            }
        }
        result
    }
}

#[test]
fn count() {
    use super::{EdgeInfo, NodeInfo};
    let g = Graph::new(vec![NodeInfo::new(1, 2.3, 3.4, 0),
                            NodeInfo::new(2, 2.3, 3.4, 0),
                            NodeInfo::new(3, 2.3, 3.4, 0),
                            NodeInfo::new(4, 2.3, 3.4, 0),
                            NodeInfo::new(5, 2.3, 3.4, 0)],
                       vec![EdgeInfo::new(0, 1, 3, 3),
                            EdgeInfo::new(0, 2, 3, 3),
                            EdgeInfo::new(2, 3, 3, 3),
                            EdgeInfo::new(4, 0, 3, 3)]);
    assert_eq!(g.count_components(), 1)
}
<|endoftext|>


<commit_before>pub mod fg{
    use std::run::Process;
    use std::run::ProcessOptions;
    // A foreground process is a command, arguments, and file descriptors for its
    // input and output.
    #[allow(dead_code)]
    pub struct FgProcess {
        command     : ~str,
        args        : ~[~str],
        stdin       : Option<i32>,
        stdout      : Option<i32>,
    }
    impl FgProcess {
        #[allow(dead_code)]
        pub fn new(program : ~str, argv: ~[~str],
            stdin : Option<i32>, stdout : Option<i32>)
            -> FgProcess
        {
            FgProcess {
                command     : program.to_owned(),
                args        : argv.to_owned(),
                stdin       : stdin,
                stdout      : stdout,
            }
        }

        #[allow(dead_code)]
        pub fn run(&mut self) -> Process {
            let command = self.command.to_owned();
            let args = self.args.to_owned();
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : self.stdin,
                out_fd : self.stdout,
                err_fd : None,
            };
            Process::new(command, args, options).expect("Couldn't run!")
        }
    }
}

// Background processes are handled differently, but not *that* differently:
// we need to keep track of an ProcessExit port for determining whether a
// process has finished running, as well as a pid for the process (for killing
// it when the shell terminates).
pub mod bg {
    use std::run::Process;
    use std::run::ProcessOptions;
    use std::io::process::ProcessExit;
    use std::libc::types::os::arch::posix88::pid_t;
    #[allow(dead_code)]
    pub struct BgProcess {
        command      : ~str,
        args         : ~[~str],
        exit_port    : Option<Port<ProcessExit>>,
        pid          : Option<i32>,
        stdin       : Option<i32>,
        stdout      : Option<i32>,
    }
    impl BgProcess {
        #[allow(dead_code)]
        pub fn new(program : ~str, argv: ~[~str]) -> BgProcess {
            BgProcess {
                command: program.to_owned(),
                args: argv.to_owned(),
                exit_port: None,
                pid: None,
                stdin: None,
                stdout: None,
            }
        }

        #[allow(dead_code)]
        pub fn run(&mut self) -> Option<pid_t> {
            // Process exit ports; used for checking dead status.
            let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
            // Process ports; these don't leave this function and are used for
            // sending the PID out in the return value.
            let (pidport, pidchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                                = Chan::new();
            let command = self.command.to_owned();
            let args = self.args.to_owned();
            spawn(proc() {
                let options = ProcessOptions {
                    env    : None,
                    dir    : None,
                    in_fd  : None,
                    out_fd : None,
                    err_fd : None,
                };
                let maybe_process = Process::new(command, args, options);
                match maybe_process {
                    Some(mut process) => {
                        // Send the pid out for the return value
                        pidchan.try_send_deferred(Some(process.get_id()));
                        chan.try_send_deferred(process.finish());
                    }
                    None => {
                        pidchan.try_send_deferred(None);
                    }
                }
            });
            self.exit_port = Some(port);
            self.pid = pidport.recv();
            self.pid
        }
    }
}
<commit_msg>remove extra dead code options<commit_after>#[allow(dead_code)]
pub mod fg{
    use std::run::Process;
    use std::run::ProcessOptions;
    // A foreground process is a command, arguments, and file descriptors for its
    // input and output.
    pub struct FgProcess {
        command     : ~str,
        args        : ~[~str],
        stdin       : Option<i32>,
        stdout      : Option<i32>,
    }
    impl FgProcess {
        pub fn new(program : ~str, argv: ~[~str],
            stdin : Option<i32>, stdout : Option<i32>)
            -> FgProcess
        {
            FgProcess {
                command     : program.to_owned(),
                args        : argv.to_owned(),
                stdin       : stdin,
                stdout      : stdout,
            }
        }

        pub fn run(&mut self) -> Process {
            let command = self.command.to_owned();
            let args = self.args.to_owned();
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : self.stdin,
                out_fd : self.stdout,
                err_fd : None,
            };
            Process::new(command, args, options).expect("Couldn't run!")
        }
    }
}

// Background processes are handled differently, but not *that* differently:
// we need to keep track of an ProcessExit port for determining whether a
// process has finished running, as well as a pid for the process (for killing
// it when the shell terminates).
#[allow(dead_code)]
pub mod bg {
    use std::run::Process;
    use std::run::ProcessOptions;
    use std::io::process::ProcessExit;
    use std::libc::types::os::arch::posix88::pid_t;
    pub struct BgProcess {
        command      : ~str,
        args         : ~[~str],
        exit_port    : Option<Port<ProcessExit>>,
        pid          : Option<i32>,
        stdin       : Option<i32>,
        stdout      : Option<i32>,
    }
    impl BgProcess {
        pub fn new(program : ~str, argv: ~[~str]) -> BgProcess {
            BgProcess {
                command: program.to_owned(),
                args: argv.to_owned(),
                exit_port: None,
                pid: None,
                stdin: None,
                stdout: None,
            }
        }

        pub fn run(&mut self) -> Option<pid_t> {
            // Process exit ports; used for checking dead status.
            let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
            // Process ports; these don't leave this function and are used for
            // sending the PID out in the return value.
            let (pidport, pidchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                                = Chan::new();
            let command = self.command.to_owned();
            let args = self.args.to_owned();
            spawn(proc() {
                let options = ProcessOptions {
                    env    : None,
                    dir    : None,
                    in_fd  : None,
                    out_fd : None,
                    err_fd : None,
                };
                let maybe_process = Process::new(command, args, options);
                match maybe_process {
                    Some(mut process) => {
                        // Send the pid out for the return value
                        pidchan.try_send_deferred(Some(process.get_id()));
                        chan.try_send_deferred(process.finish());
                    }
                    None => {
                        pidchan.try_send_deferred(None);
                    }
                }
            });
            self.exit_port = Some(port);
            self.pid = pidport.recv();
            self.pid
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Dmytro Milinevskyi <dmilinevskyi@gmail.com>

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate parking_lot;
use self::parking_lot::Mutex;

use std::fs::{File, OpenOptions, create_dir_all, rename};
use std::path::{Path, PathBuf};
use std::io;
use std::io::Write;

use handlers::Handler;

/// The errors that might occur during creation of the handler.
#[derive(Debug)]
pub enum RotatingFileHandlerError {
    /// Any kind of I/O Error.
    IoError(io::Error),
    /// Log file size is invalid.
    SizeError(u64),
    /// Log file count is invalid.
    CountError(usize),
}

impl From<io::Error> for RotatingFileHandlerError {
    fn from(e: io::Error) -> Self {
        RotatingFileHandlerError::IoError(e)
    }
}

struct Context {
    path: PathBuf,
    logs: Vec<PathBuf>,
    size: u64,
    current: u64,
    file: File,
}

impl Context {
    fn open(path: &Path) -> Result<File, RotatingFileHandlerError> {
        match OpenOptions::new().append(true).create(true).open(path) {
            Ok(file) => Ok(file),
            Err(err) => Err(err.into()),
        }
    }

    fn logs(path: &Path, count: usize) -> Vec<PathBuf> {
        (0..count-1).rev().map(|r| PathBuf::from(format!("{}.{}", path.display(), r))).collect()
    }

    fn new(path: &Path, count: usize, size: u64) -> Result<Self, RotatingFileHandlerError> {
        if let Some(dir) = path.parent() {
            create_dir_all(dir)?;
        }
        let file = Self::open(path)?;
        if size == 0 {
            return Err(RotatingFileHandlerError::SizeError(size));
        }
        if count < 2 {
            return Err(RotatingFileHandlerError::CountError(count));
        }
        Ok(Context {
            path: path.into(),
            logs: Self::logs(path, count),
            size: size,
            current: file.metadata()?.len(),
            file: file,
        })
    }

    fn ____emit(&mut self, msg: &[u8]) {
        let _ = self.file.write_all(msg);
        self.current += msg.len() as u64;
    }

    fn __emit(&mut self, msg: String) {
        self.____emit(msg.as_bytes())
    }

    fn rotate(&mut self) {
        let rlen = self.logs.len();

        for i in 1..rlen {
            let res = {
                let old = &self.logs[i];
                if old.exists() {
                    rename(old, &self.logs[i - 1])
                } else {
                    Ok(())
                }
            };

            if res.is_err() {
                let msg = {
                    let old = self.logs[i].display();
                    let new = self.logs[i - 1].display();
                    format!("Failed to rename {} into {}: {}", old, new, res.unwrap_err())
                };
                self.__emit(msg);
            }
        }

        let _ = self.file.flush();

        if let Err(err) = rename(&self.path, &self.logs[rlen - 1]) {
            let msg = {
                let old = self.path.display();
                let new = self.logs[rlen - 1].display();
                format!("Failed to rename {} into {}: {}", old, new, err)
            };
            self.__emit(msg);
        }
    }

    fn emit_check(&mut self, msg: &[u8]) -> Result<(), String> {
        self.____emit(msg);
        if self.current >= self.size {
            self.rotate();

            match Self::open(&self.path) {
                Ok(file) => {
                    self.file = file;
                    self.current = 0;
                    Ok(())
                },
                Err(err) => {
                    Err(format!("Failed to open {}: {:?}", self.path.display(), err))
                }
            }
        } else {
            Ok(())
        }
    }

    fn emit(&mut self, msg: &[u8]) {
        let _ = self.emit_check(msg);
    }
}

/// Pushes log record into a file.
///
/// The directories to the log file are created automatically.
///
/// Rotates the log file is it exceed the given `size` (in bytes).
///
/// Maintains up to `count` log files.
///
/// Each log file after rotation has a numeric suffix.
pub fn handler(path: &Path, count: usize, size: u64) -> Result<Handler, RotatingFileHandlerError> {
    let ctx = Context::new(path, count, size)?;
    let ctx = Mutex::new(ctx);
    Ok(Box::new(move |record| {
        let mut ctx = ctx.lock();
        ctx.emit(record.formatted().as_bytes());
    }))
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use self::tempdir::TempDir;

    use super::*;

    use std::fs;
    use std::fs::File;
    use std::io::Read;

    fn push_check(ctx: &mut super::Context, size: u64) -> Result<(), String> {
        ctx.emit_check("x".repeat(size as usize).as_bytes())
    }

    fn push(ctx: &mut super::Context, size: u64) {
        let _ = push_check(ctx, size);
    }

    fn tlogs(logs: &[PathBuf], size: u64, filled: usize) {
        for i in 0..filled {
            let log = &logs[i];
            assert!(log.exists());
            assert_eq!(log.metadata().unwrap().len(), size);
        }
        for i in filled..logs.len() {
            assert!(!logs[i].exists());
        }
    }

    #[cfg(not(target_os = "windows"))]
    fn setro(path: &Path, ro: bool) {
        let mut perms = fs::metadata(path).unwrap().permissions();
        perms.set_readonly(ro);
        fs::set_permissions(path, perms).unwrap();
    }

    #[cfg(not(target_os = "windows"))]
    fn contains(path: &Path, needle: &String) -> bool {
        let mut log = File::open(path).unwrap();
        let mut res = String::new();
        log.read_to_string(&mut res).unwrap();
        res.contains(needle)
    }

    #[test]
    fn test_rotating_file() {
        let dir = TempDir::new("wp-rf").unwrap();
        let path = dir.path().join("logs").join("test.log");
        let count = 5;
        let size = 20;
        let mut ctx = super::Context::new(&path, count, size).unwrap();
        let mut logs = super::Context::logs(&path, count + 1);
        logs.reverse();
        let elogs = &logs[..logs.len() - 1];
        let flog = &logs[logs.len() - 1];

        assert!(path.exists());
        assert_eq!(path.metadata().unwrap().len(), 0);

        push(&mut ctx, size / 2);
        assert_eq!(path.metadata().unwrap().len(), size / 2);
        tlogs(elogs, size, 0);

        push(&mut ctx, size / 2);
        assert_eq!(path.metadata().unwrap().len(), 0);
        tlogs(elogs, size, 1);

        for i in 2..count {
            push(&mut ctx, size);
            assert_eq!(path.metadata().unwrap().len(), 0);
            tlogs(elogs, size, i);
        }

        assert!(!flog.exists());
    }

    #[test]
    fn test_rotating_file_invalid() {
        let dir = TempDir::new("wp-f").unwrap();
        let ctx = Context::new(&dir.path(), 1, 1);
        let err = ctx.err().unwrap();

        assert!(format!("{:?}", err).contains("directory"));

        let path = dir.path().join("logs").join("test.log");

        for count in 0..2 {
            let err = Context::new(&path, count, 1).err().unwrap();
            assert!(format!("{:?}", err).contains("CountError"));
        }

        let err = Context::new(&path, 1, 0).err().unwrap();
        assert!(format!("{:?}", err).contains("SizeError"));
    }

    #[cfg(not(target_os = "windows"))]
    #[test]
    fn test_rotating_file_failure() {
        let dir = TempDir::new("wp-f").unwrap();
        let path = dir.path().join("test.log");
        let count = 3;
        let mut ctx = Context::new(&path, count, 1).unwrap();
        let mut logs = super::Context::logs(&path, count);
        logs.reverse();

        // Fail rename of origin to `.x`
        setro(&dir.path(), true);
        push(&mut ctx, 2);
        for log in &logs {
            assert!(!log.exists());
        }

        // Fail rename from `.x` to `.y`
        setro(&dir.path(), false);
        push(&mut ctx, 2);
        setro(&dir.path(), true);
        push(&mut ctx, 2);
        assert!(logs[0].exists());
        for idx in 1..logs.len() {
            assert!(!logs[idx].exists());
        }
        let expect = "Failed to rename".to_string();
        assert!(contains(&path, &expect));
        for idx in 0..1 {
            assert!(contains(&logs[idx], &expect));
        }

        // Fail to open origin
        setro(&dir.path(), false);
        dir.close().unwrap();
        assert!(push_check(&mut ctx, 2).err().unwrap().contains("Failed to open"));
    }
}
<commit_msg>handlers::rotating_file: skip test_rotating_file_failure on windows (attempt #2)<commit_after>// Copyright 2017 Dmytro Milinevskyi <dmilinevskyi@gmail.com>

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate parking_lot;
use self::parking_lot::Mutex;

use std::fs::{File, OpenOptions, create_dir_all, rename};
use std::path::{Path, PathBuf};
use std::io;
use std::io::Write;

use handlers::Handler;

/// The errors that might occur during creation of the handler.
#[derive(Debug)]
pub enum RotatingFileHandlerError {
    /// Any kind of I/O Error.
    IoError(io::Error),
    /// Log file size is invalid.
    SizeError(u64),
    /// Log file count is invalid.
    CountError(usize),
}

impl From<io::Error> for RotatingFileHandlerError {
    fn from(e: io::Error) -> Self {
        RotatingFileHandlerError::IoError(e)
    }
}

struct Context {
    path: PathBuf,
    logs: Vec<PathBuf>,
    size: u64,
    current: u64,
    file: File,
}

impl Context {
    fn open(path: &Path) -> Result<File, RotatingFileHandlerError> {
        match OpenOptions::new().append(true).create(true).open(path) {
            Ok(file) => Ok(file),
            Err(err) => Err(err.into()),
        }
    }

    fn logs(path: &Path, count: usize) -> Vec<PathBuf> {
        (0..count-1).rev().map(|r| PathBuf::from(format!("{}.{}", path.display(), r))).collect()
    }

    fn new(path: &Path, count: usize, size: u64) -> Result<Self, RotatingFileHandlerError> {
        if let Some(dir) = path.parent() {
            create_dir_all(dir)?;
        }
        let file = Self::open(path)?;
        if size == 0 {
            return Err(RotatingFileHandlerError::SizeError(size));
        }
        if count < 2 {
            return Err(RotatingFileHandlerError::CountError(count));
        }
        Ok(Context {
            path: path.into(),
            logs: Self::logs(path, count),
            size: size,
            current: file.metadata()?.len(),
            file: file,
        })
    }

    fn ____emit(&mut self, msg: &[u8]) {
        let _ = self.file.write_all(msg);
        self.current += msg.len() as u64;
    }

    fn __emit(&mut self, msg: String) {
        self.____emit(msg.as_bytes())
    }

    fn rotate(&mut self) {
        let rlen = self.logs.len();

        for i in 1..rlen {
            let res = {
                let old = &self.logs[i];
                if old.exists() {
                    rename(old, &self.logs[i - 1])
                } else {
                    Ok(())
                }
            };

            if res.is_err() {
                let msg = {
                    let old = self.logs[i].display();
                    let new = self.logs[i - 1].display();
                    format!("Failed to rename {} into {}: {}", old, new, res.unwrap_err())
                };
                self.__emit(msg);
            }
        }

        let _ = self.file.flush();

        if let Err(err) = rename(&self.path, &self.logs[rlen - 1]) {
            let msg = {
                let old = self.path.display();
                let new = self.logs[rlen - 1].display();
                format!("Failed to rename {} into {}: {}", old, new, err)
            };
            self.__emit(msg);
        }
    }

    fn emit_check(&mut self, msg: &[u8]) -> Result<(), String> {
        self.____emit(msg);
        if self.current >= self.size {
            self.rotate();

            match Self::open(&self.path) {
                Ok(file) => {
                    self.file = file;
                    self.current = 0;
                    Ok(())
                },
                Err(err) => {
                    Err(format!("Failed to open {}: {:?}", self.path.display(), err))
                }
            }
        } else {
            Ok(())
        }
    }

    fn emit(&mut self, msg: &[u8]) {
        let _ = self.emit_check(msg);
    }
}

/// Pushes log record into a file.
///
/// The directories to the log file are created automatically.
///
/// Rotates the log file is it exceed the given `size` (in bytes).
///
/// Maintains up to `count` log files.
///
/// Each log file after rotation has a numeric suffix.
pub fn handler(path: &Path, count: usize, size: u64) -> Result<Handler, RotatingFileHandlerError> {
    let ctx = Context::new(path, count, size)?;
    let ctx = Mutex::new(ctx);
    Ok(Box::new(move |record| {
        let mut ctx = ctx.lock();
        ctx.emit(record.formatted().as_bytes());
    }))
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use self::tempdir::TempDir;

    use super::*;

    use std::fs;
    use std::fs::File;
    use std::io::Read;

    fn push_check(ctx: &mut super::Context, size: u64) -> Result<(), String> {
        ctx.emit_check("x".repeat(size as usize).as_bytes())
    }

    fn push(ctx: &mut super::Context, size: u64) {
        let _ = push_check(ctx, size);
    }

    fn tlogs(logs: &[PathBuf], size: u64, filled: usize) {
        for i in 0..filled {
            let log = &logs[i];
            assert!(log.exists());
            assert_eq!(log.metadata().unwrap().len(), size);
        }
        for i in filled..logs.len() {
            assert!(!logs[i].exists());
        }
    }

    #[cfg(unix)]
    fn setro(path: &Path, ro: bool) {
        let mut perms = fs::metadata(path).unwrap().permissions();
        perms.set_readonly(ro);
        fs::set_permissions(path, perms).unwrap();
    }

    #[cfg(unix)]
    fn contains(path: &Path, needle: &String) -> bool {
        let mut log = File::open(path).unwrap();
        let mut res = String::new();
        log.read_to_string(&mut res).unwrap();
        res.contains(needle)
    }

    #[test]
    fn test_rotating_file() {
        let dir = TempDir::new("wp-rf").unwrap();
        let path = dir.path().join("logs").join("test.log");
        let count = 5;
        let size = 20;
        let mut ctx = super::Context::new(&path, count, size).unwrap();
        let mut logs = super::Context::logs(&path, count + 1);
        logs.reverse();
        let elogs = &logs[..logs.len() - 1];
        let flog = &logs[logs.len() - 1];

        assert!(path.exists());
        assert_eq!(path.metadata().unwrap().len(), 0);

        push(&mut ctx, size / 2);
        assert_eq!(path.metadata().unwrap().len(), size / 2);
        tlogs(elogs, size, 0);

        push(&mut ctx, size / 2);
        assert_eq!(path.metadata().unwrap().len(), 0);
        tlogs(elogs, size, 1);

        for i in 2..count {
            push(&mut ctx, size);
            assert_eq!(path.metadata().unwrap().len(), 0);
            tlogs(elogs, size, i);
        }

        assert!(!flog.exists());
    }

    #[test]
    fn test_rotating_file_invalid() {
        let dir = TempDir::new("wp-f").unwrap();
        let ctx = Context::new(&dir.path(), 1, 1);
        let err = ctx.err().unwrap();

        assert!(format!("{:?}", err).contains("directory"));

        let path = dir.path().join("logs").join("test.log");

        for count in 0..2 {
            let err = Context::new(&path, count, 1).err().unwrap();
            assert!(format!("{:?}", err).contains("CountError"));
        }

        let err = Context::new(&path, 1, 0).err().unwrap();
        assert!(format!("{:?}", err).contains("SizeError"));
    }

    #[cfg(unix)]
    #[test]
    fn test_rotating_file_failure() {
        let dir = TempDir::new("wp-f").unwrap();
        let path = dir.path().join("test.log");
        let count = 3;
        let mut ctx = Context::new(&path, count, 1).unwrap();
        let mut logs = super::Context::logs(&path, count);
        logs.reverse();

        // Fail rename of origin to `.x`
        setro(&dir.path(), true);
        push(&mut ctx, 2);
        for log in &logs {
            assert!(!log.exists());
        }

        // Fail rename from `.x` to `.y`
        setro(&dir.path(), false);
        push(&mut ctx, 2);
        setro(&dir.path(), true);
        push(&mut ctx, 2);
        assert!(logs[0].exists());
        for idx in 1..logs.len() {
            assert!(!logs[idx].exists());
        }
        let expect = "Failed to rename".to_string();
        assert!(contains(&path, &expect));
        for idx in 0..1 {
            assert!(contains(&logs[idx], &expect));
        }

        // Fail to open origin
        setro(&dir.path(), false);
        dir.close().unwrap();
        assert!(push_check(&mut ctx, 2).err().unwrap().contains("Failed to open"));
    }
}
<|endoftext|>