<commit_before>use std::thread;
use std::time::{Duration, Instant};

use sdl2;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

use sdl2::pixels::Color;
use sdl2::rect::Rect; // TODO: abstract my own drawtypes?
use sdl2::render::Texture;

use graphics::Display;
use input::Input;
use units::V2;

pub static COLOR_BG: Color  = Color::RGB(0,0,0);
pub static COLOR_FPS: Color = Color::RGB(255,255,0);
pub static COLOR_PEN: Color = Color::RGB(125, 0, 175);

#[derive(Copy, Clone, Debug)]
enum BrushMode {
    Normal,
    Squareish,
    WowSoEdgy,
}

pub struct Engine {
	context:     sdl2::Sdl,
	controller:  Input,
	display:     Display,

    brush:   BrushMode,
    color:   (u8,u8,u8),
    cursor:  (i32,i32),
    scanbox: V2,
}

impl Engine {
    pub fn new(context: sdl2::Sdl) -> Engine {
        let video_renderer = Display::new(&context);

        Engine {
            context:    context,
            controller: Input::new(),
            display:    video_renderer,

            brush:   BrushMode::Normal,
            color:   (125,0,175),
            cursor:  (0,0),
            scanbox: V2(0,0),
        }
    }

    pub fn run(&mut self) {
        let target_fps_ms  = Duration::from_millis(1000 / 120); // TODO: const fn?

		let mut event_pump = self.context.event_pump().unwrap();
        let mut is_running = true;

        let mut frame_start_at;
        let mut elapsed_time = Duration::from_millis(0);

        // drawing state
        let mut regions = vec![];
        for _ in 0..9 {
            let mut texture = Some(self.display.get_texture(1280,720));
            Engine::with_texture(&mut self.display, &mut texture, |io| { io.clear_buffer() });
            regions.push(texture);
        }

        let mut mouse_clicked = false;
        let mut last_point = None;

        // init bitmap to good state
        //

        while is_running {
            frame_start_at  = Instant::now();

			// drain input event queue once per frame
			self.controller.begin_new_frame();
			for event in event_pump.poll_iter() {
				match event {
					Event::KeyDown { keycode, .. } => {
						self.controller.key_down_event(keycode.unwrap());
					},

					Event::KeyUp { keycode, .. } => {
						self.controller.key_up_event(keycode.unwrap());
					},

                    Event::MouseMotion { x, y, .. } => self.cursor = (x,y),

                    Event::MouseButtonDown { .. } => mouse_clicked = true,
                    Event::MouseButtonUp   { .. } => mouse_clicked = false,

					_ => {},
				}
			}

            // handle exit game
			if self.controller.was_key_released(Keycode::Escape) { is_running = false; }

            // erase canvas
            if self.controller.was_key_released(Keycode::E) {
                Engine::with_texture(&mut self.display, &mut regions[0], |io| { io.clear_buffer() });
            }

            // switch brush
            if self.controller.was_key_released(Keycode::B) {
                self.brush = match self.brush { // TODO: better cycle?
                    BrushMode::Normal    => BrushMode::Squareish,
                    BrushMode::Squareish => BrushMode::WowSoEdgy,
                    BrushMode::WowSoEdgy => BrushMode::Squareish,
                }
            }

            if self.controller.is_key_held(Keycode::I) {
                self.color.0 = self.color.0.wrapping_add(0x01);
            } else if self.controller.is_key_held(Keycode::O) {
                self.color.1 = self.color.1.wrapping_add(0x01);
            } else if self.controller.is_key_held(Keycode::P) {
                self.color.2 = self.color.2.wrapping_add(0x01);
            }

            if self.controller.is_key_held(Keycode::Up) {
                self.scanbox = self.scanbox - V2(0, 1);
            } else if self.controller.is_key_held(Keycode::Down) {
                self.scanbox = self.scanbox + V2(0, 1);
            } else if self.controller.is_key_held(Keycode::Left) {
                self.scanbox = self.scanbox - V2(1, 0);
            } else if self.controller.is_key_held(Keycode::Right) {
                self.scanbox = self.scanbox + V2(1, 0);
            }

            let brush_color = Color::RGB(self.color.0, self.color.1, self.color.2);
            if mouse_clicked {
                let (x2,y2) = self.cursor;

                if let Some((x1,y1)) = last_point {

                    let brush = self.brush;
                    Engine::with_texture(&mut self.display, &mut regions[0], |display| {
                        match brush {
                            BrushMode::WowSoEdgy => for i in 0..10 {
                                display.draw_line(x1+i, y1, x2, y2+i, brush_color);
                            },

                            BrushMode::Squareish => for i in 0..5 {
                                display.draw_line(x1+i, y1, x2+i, y2, brush_color);
                            },

                            BrushMode::Normal => {},
                        }
                    });

                    // let diffx = x2 - x1;
                    // let diffy = y2 - y1;
                    // println!("delta ({},{}), mag: {}", diffx, diffy, diffy-diffx);
                } // NOTE: doesn't draw point if mouse held for single frame

                last_point = Some((x2,y2));
           } else { last_point = None; }

            // handle draw calls
			self.display.clear_buffer(); // clear back-buffer
            self.draw_regions(&mut regions);
            self.draw_cursor(brush_color);
            self.draw_debug(elapsed_time);
			self.display.switch_buffers();

            // sleep for <target> - <draw time> and floor to zero
            elapsed_time = frame_start_at.elapsed();
            let sleep_time = if elapsed_time > target_fps_ms {
                Duration::from_millis(0)
            } else { target_fps_ms - elapsed_time };

            thread::sleep(sleep_time);
        }
    }

    // HACK: pulls texture out of an option and gives you a closure in which
    // you can use renderer calls to blit to it ...
    //
    fn with_texture<F>(io: &mut Display, target: &mut Option<Texture>, mut mutator: F)
        where F: FnMut(&mut Display) -> () {

        let _last = io.retarget().set(target.take().unwrap());
        mutator(io);
        *target = io.retarget().reset()
                        .ok().expect("did not get target back");
    }

    fn draw_cursor(&mut self, brush_color: Color) {
        self.display.fill_rect(Rect::new(self.cursor.0, self.cursor.1, 10, 10), brush_color);
    }

    fn draw_debug(&mut self, time: Duration) {
        let mut time_ms = time.as_secs() * 1000;        // -> millis
        time_ms += time.subsec_nanos() as u64 / (1000 * 1000); // /> micros /> millis

        let (hue_r, hue_g, hue_b) = self.color;
        let buf = format!("{}ms, sb @ {:?}, e = erase all, b = brush ({:?}), hue(i,o,p) => ({:x},{:x},{:x})",
                          time_ms,
                          self.scanbox,
                          self.brush,
                          hue_r, hue_g, hue_b);
        self.display.blit_text(&buf[..], COLOR_FPS);
    }

    fn draw_regions(&mut self, regions: &mut Vec<Option<Texture>>) {
        for row in 0..1 {
            for col in 0..1 {
                // (0,0), (0, 720)
                let x = (col * 1280) as i32;
                let y = (row *  720) as i32;
                let ridx = col + (row * 3);

                println!("drawing [{}] at ({},{})", ridx, x, y);
                self.display.copy_t(regions[ridx].as_ref().unwrap(),
                    Rect::new(0, 0, 1280, 720),
                    Rect::new(x, y, 1280, 720));
            }
        }
    }
}
<commit_msg>draw all regions in screenspace<commit_after>use std::thread;
use std::time::{Duration, Instant};

use sdl2;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

use sdl2::pixels::Color;
use sdl2::rect::Rect; // TODO: abstract my own drawtypes?
use sdl2::render::Texture;

use graphics::Display;
use input::Input;
use units::V2;

pub static COLOR_BG: Color  = Color::RGB(0,0,0);
pub static COLOR_FPS: Color = Color::RGB(255,255,0);
pub static COLOR_PEN: Color = Color::RGB(125, 0, 175);

#[derive(Copy, Clone, Debug)]
enum BrushMode {
    Normal,
    Squareish,
    WowSoEdgy,
}

pub struct Engine {
	context:     sdl2::Sdl,
	controller:  Input,
	display:     Display,

    brush:   BrushMode,
    color:   (u8,u8,u8),
    cursor:  (i32,i32),
    scanbox: V2,
}

impl Engine {
    pub fn new(context: sdl2::Sdl) -> Engine {
        let video_renderer = Display::new(&context);

        Engine {
            context:    context,
            controller: Input::new(),
            display:    video_renderer,

            brush:   BrushMode::Normal,
            color:   (125,0,175),
            cursor:  (0,0),
            scanbox: V2(0,0),
        }
    }

    pub fn run(&mut self) {
        let target_fps_ms  = Duration::from_millis(1000 / 120); // TODO: const fn?

		let mut event_pump = self.context.event_pump().unwrap();
        let mut is_running = true;

        let mut frame_start_at;
        let mut elapsed_time = Duration::from_millis(0);

        // drawing state
        let mut regions = vec![];
        for _ in 0..9 {
            let mut texture = Some(self.display.get_texture(1280,720));
            Engine::with_texture(&mut self.display, &mut texture, |io| {
                io.clear_buffer();
                io.fill_rect(Rect::new(0,0,1280,5),   Color::RGB(255,0,0));  // top
                io.fill_rect(Rect::new(0,715,1280,5), Color::RGB(255,0,0));  // bottom
                io.fill_rect(Rect::new(0,0,5,720),    Color::RGB(255,0,0) ); // left
                io.fill_rect(Rect::new(1275,0,5,720), Color::RGB(255,0,0));  // right
            });

            regions.push(texture);
        }

        let mut mouse_clicked = false;
        let mut last_point = None;

        // init bitmap to good state
        //

        while is_running {
            frame_start_at  = Instant::now();

			// drain input event queue once per frame
			self.controller.begin_new_frame();
			for event in event_pump.poll_iter() {
				match event {
					Event::KeyDown { keycode, .. } => {
						self.controller.key_down_event(keycode.unwrap());
					},

					Event::KeyUp { keycode, .. } => {
						self.controller.key_up_event(keycode.unwrap());
					},

                    Event::MouseMotion { x, y, .. } => self.cursor = (x,y),

                    Event::MouseButtonDown { .. } => mouse_clicked = true,
                    Event::MouseButtonUp   { .. } => mouse_clicked = false,

					_ => {},
				}
			}

            // handle exit game
			if self.controller.was_key_released(Keycode::Escape) { is_running = false; }

            // erase canvas
            if self.controller.was_key_released(Keycode::E) {
                Engine::with_texture(&mut self.display, &mut regions[0], |io| { io.clear_buffer() });
            }

            // switch brush
            if self.controller.was_key_released(Keycode::B) {
                self.brush = match self.brush { // TODO: better cycle?
                    BrushMode::Normal    => BrushMode::Squareish,
                    BrushMode::Squareish => BrushMode::WowSoEdgy,
                    BrushMode::WowSoEdgy => BrushMode::Squareish,
                }
            }

            if self.controller.is_key_held(Keycode::I) {
                self.color.0 = self.color.0.wrapping_add(0x01);
            } else if self.controller.is_key_held(Keycode::O) {
                self.color.1 = self.color.1.wrapping_add(0x01);
            } else if self.controller.is_key_held(Keycode::P) {
                self.color.2 = self.color.2.wrapping_add(0x01);
            }

            if self.controller.is_key_held(Keycode::Up) {
                self.scanbox = self.scanbox - V2(0, 1);
            } else if self.controller.is_key_held(Keycode::Down) {
                self.scanbox = self.scanbox + V2(0, 1);
            } else if self.controller.is_key_held(Keycode::Left) {
                self.scanbox = self.scanbox - V2(1, 0);
            } else if self.controller.is_key_held(Keycode::Right) {
                self.scanbox = self.scanbox + V2(1, 0);
            }

            let brush_color = Color::RGB(self.color.0, self.color.1, self.color.2);
            if mouse_clicked {
                let (x2,y2) = self.cursor;

                if let Some((x1,y1)) = last_point {

                    let brush = self.brush;
                    Engine::with_texture(&mut self.display, &mut regions[0], |display| {
                        match brush {
                            BrushMode::WowSoEdgy => for i in 0..10 {
                                display.draw_line(x1+i, y1, x2, y2+i, brush_color);
                            },

                            BrushMode::Squareish => for i in 0..5 {
                                display.draw_line(x1+i, y1, x2+i, y2, brush_color);
                            },

                            BrushMode::Normal => {},
                        }
                    });

                    // let diffx = x2 - x1;
                    // let diffy = y2 - y1;
                    // println!("delta ({},{}), mag: {}", diffx, diffy, diffy-diffx);
                } // NOTE: doesn't draw point if mouse held for single frame

                last_point = Some((x2,y2));
           } else { last_point = None; }

            // handle draw calls
			self.display.clear_buffer(); // clear back-buffer
            self.draw_regions(&mut regions);
            self.draw_cursor(brush_color);
            self.draw_debug(elapsed_time);
			self.display.switch_buffers();

            // sleep for <target> - <draw time> and floor to zero
            elapsed_time = frame_start_at.elapsed();
            let sleep_time = if elapsed_time > target_fps_ms {
                Duration::from_millis(0)
            } else { target_fps_ms - elapsed_time };

            thread::sleep(sleep_time);
        }
    }

    // HACK: pulls texture out of an option and gives you a closure in which
    // you can use renderer calls to blit to it ...
    //
    fn with_texture<F>(io: &mut Display, target: &mut Option<Texture>, mut mutator: F)
        where F: FnMut(&mut Display) -> () {

        let _last = io.retarget().set(target.take().unwrap());
        mutator(io);
        *target = io.retarget().reset()
                        .ok().expect("did not get target back");
    }

    fn draw_cursor(&mut self, brush_color: Color) {
        self.display.fill_rect(Rect::new(self.cursor.0, self.cursor.1, 10, 10), brush_color);
    }

    fn draw_debug(&mut self, time: Duration) {
        let mut time_ms = time.as_secs() * 1000;        // -> millis
        time_ms += time.subsec_nanos() as u64 / (1000 * 1000); // /> micros /> millis

        let (hue_r, hue_g, hue_b) = self.color;
        let buf = format!("{}ms, sb @ {:?}, e = erase all, b = brush ({:?}), hue(i,o,p) => ({:x},{:x},{:x})",
                          time_ms,
                          self.scanbox,
                          self.brush,
                          hue_r, hue_g, hue_b);
        self.display.blit_text(&buf[..], COLOR_FPS);
    }

    fn draw_regions(&mut self, regions: &mut Vec<Option<Texture>>) {
        let V2(ofs_x, ofs_y) = self.scanbox;

        for row in 0..3 {
            for col in 0..3 {
                // (0,0), (0, 720)
                let x = (col * 1280) as i32;
                let y = (row *  720) as i32;
                let ridx = col + (row * 3);

                println!("drawing [{}] at ({},{})", ridx, x, y);
                self.display.copy_t(regions[ridx].as_ref().unwrap(),
                    Rect::new(0, 0, 1280, 720),
                    Rect::new(x - ofs_x as i32, y - ofs_y as i32, 1280, 720));
            }
        }
    }
}
<|endoftext|>


<commit_before>use MU;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use na::{Point2, Vector2, Isometry2, Translation};
use na;
use ncollide::world::{GeometricQueryType, CollisionGroups, CollisionWorld};
use ncollide::shape::{Cuboid, ShapeHandle2};
use action::Action;
use graphics_component::GraphicsComponent;

pub type CollideWorld = CollisionWorld<Point2<f32>, Isometry2<f32>, Entity>;

#[derive(Debug, Clone, PartialEq)]
pub enum EntityType {
    Character,
    Wall
}

#[derive(Clone)]
pub struct Entity {
    color: [f32; 4],
    velocity: Option<Rc<Cell<Vector2<f32>>>>,
    width: f32,
    height: f32,
    world: Rc<RefCell<CollideWorld>>,
    index: usize,
    pub entity_type: EntityType,
    pub graphics_component: GraphicsComponent
}

impl Entity {
    pub fn new(pos: Point2<f32>, color: [f32; 4],
        w: f32, h: f32, velocity: Option<Vector2<f32>>,
        world: Rc<RefCell<CollideWorld>>,
        idx: usize, entity_type: EntityType,
        graphics_component: GraphicsComponent) -> Self {

        let entity = Entity {
            color: color,
            velocity: velocity.map(|v| Rc::new(Cell::new(v))),
            width: w,
            height: h,
            world: world.clone(),
            index: idx,
            entity_type: entity_type,
            graphics_component: graphics_component
        };

        let rect = ShapeHandle2::new(Cuboid::new(Vector2::new(w / 2.0, h / 2.0)));

        let pos = Isometry2::new(pos.to_vector(), na::zero());

        let mut groups = CollisionGroups::new();
        groups.set_membership(&[1]);

        let mut world = world.borrow_mut();

        world.add(idx, pos, rect, groups, GeometricQueryType::Contacts(0.0), entity.clone());
        world.update();

        entity
    }

    pub fn geometry(&self) -> [f64; 4] {
        let (x, y) = self.position();

        [x as f64, y as f64, self.width as f64, self.height as f64]
    }

    pub fn x_pos(&self) -> f64 {
        self.geometry()[0]
    }

    pub fn y_pos(&self) -> f64 {
        self.geometry()[1]
    }

    pub fn color(&self) -> [f32; 4] {
        self.color
    }

    pub fn velocity(&self) -> &Option<Rc<Cell<Vector2<f32>>>> {
        &self.velocity
    }

    pub fn nudge(&mut self) {
        if let Some(ref delta_v) = self.velocity {
            let dv = delta_v.get();
            let mut world = self.world.borrow_mut();
            let pos = world.collision_object(self.index).unwrap().position;
            let new_pos = pos.append_translation(&dv);

            world.deferred_set_position(self.index, new_pos);
            delta_v.set(dv * MU);
        }
    }

    pub fn handle_input(&mut self, action: Action) {
        match action {
            Action::Up     => self.adjust_dy(-1.0),
            Action::Down   => self.adjust_dy(1.0),
            Action::Left   => self.adjust_dx(-1.0),
            Action::Right  => self.adjust_dx(1.0),
            _              => {}
        }
    }

    pub fn adjust_dx(&mut self, dx: f32) {
        if let Some(ref delta_v) = self.velocity {
            delta_v.set(delta_v.get() + Vector2::new(dx, 0.0));
        }
    }

    pub fn adjust_dy(&mut self, dy: f32) {
        if let Some(ref delta_v) = self.velocity {
            delta_v.set(delta_v.get() + Vector2::new(0.0, dy));
        }
    }

    fn position(&self) -> (f32, f32) {
        let world = self.world.borrow();
        let pos = world.collision_object(self.index).unwrap().position.translation;

        (pos.x - self.width / 2.0, pos.y - self.height / 2.0)
    }

}
<commit_msg>More refactoring<commit_after>use MU;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use na::{Point2, Vector2, Isometry2, Translation};
use na;
use ncollide::world::{GeometricQueryType, CollisionGroups, CollisionWorld};
use ncollide::shape::{Cuboid, ShapeHandle2};
use action::Action;
use graphics_component::GraphicsComponent;

const DELTA_V: f32 = 1.0;

pub type CollideWorld = CollisionWorld<Point2<f32>, Isometry2<f32>, Entity>;

#[derive(Debug, Clone, PartialEq)]
pub enum EntityType {
    Character,
    Wall
}

#[derive(Clone)]
pub struct Entity {
    color: [f32; 4],
    velocity: Option<Rc<Cell<Vector2<f32>>>>,
    width: f32,
    height: f32,
    world: Rc<RefCell<CollideWorld>>,
    index: usize,
    pub entity_type: EntityType,
    pub graphics_component: GraphicsComponent
}

impl Entity {
    pub fn new(pos: Point2<f32>, color: [f32; 4],
        w: f32, h: f32, velocity: Option<Vector2<f32>>,
        world: Rc<RefCell<CollideWorld>>,
        idx: usize, entity_type: EntityType,
        graphics_component: GraphicsComponent) -> Self {

        let entity = Entity {
            color: color,
            velocity: velocity.map(|v| Rc::new(Cell::new(v))),
            width: w,
            height: h,
            world: world.clone(),
            index: idx,
            entity_type: entity_type,
            graphics_component: graphics_component
        };

        let rect = ShapeHandle2::new(Cuboid::new(Vector2::new(w / 2.0, h / 2.0)));

        let pos = Isometry2::new(pos.to_vector(), na::zero());

        let mut groups = CollisionGroups::new();
        groups.set_membership(&[1]);

        let mut world = world.borrow_mut();

        world.add(idx, pos, rect, groups, GeometricQueryType::Contacts(0.0), entity.clone());
        world.update();

        entity
    }

    pub fn geometry(&self) -> [f64; 4] {
        let (x, y) = self.position();

        [x as f64, y as f64, self.width as f64, self.height as f64]
    }

    pub fn x_pos(&self) -> f64 {
        self.geometry()[0]
    }

    pub fn y_pos(&self) -> f64 {
        self.geometry()[1]
    }

    pub fn color(&self) -> [f32; 4] {
        self.color
    }

    pub fn velocity(&self) -> &Option<Rc<Cell<Vector2<f32>>>> {
        &self.velocity
    }

    pub fn nudge(&mut self) {
        if let Some(ref delta_v) = self.velocity {
            let dv = delta_v.get();
            let mut world = self.world.borrow_mut();
            let pos = world.collision_object(self.index).unwrap().position;
            let new_pos = pos.append_translation(&dv);

            world.deferred_set_position(self.index, new_pos);
            delta_v.set(dv * MU);
        }
    }

    pub fn handle_input(&mut self, action: Action) {
        match action {
            Action::Up     => self.adjust_dy(-DELTA_V),
            Action::Down   => self.adjust_dy(DELTA_V),
            Action::Left   => self.adjust_dx(-DELTA_V),
            Action::Right  => self.adjust_dx(DELTA_V),
            _              => {}
        }
    }

    pub fn adjust_dx(&mut self, dx: f32) {
        self.adjust_dv(dx, 0.0);
    }

    pub fn adjust_dy(&mut self, dy: f32) {
        self.adjust_dv(0.0, dy);
    }

    fn adjust_dv(&mut self, dx: f32, dy: f32) {
        if let Some(ref delta_v) = self.velocity {
            delta_v.set(delta_v.get() + Vector2::new(dx, dy));
        }
    }

    fn position(&self) -> (f32, f32) {
        let world = self.world.borrow();
        let pos = world.collision_object(self.index).unwrap().position.translation;

        (pos.x - self.width / 2.0, pos.y - self.height / 2.0)
    }

}
<|endoftext|>


<commit_before>use std::str;
use network::{ActorId, ObjectId, StreamId};
use attributes::Attribute;

#[derive(PartialEq, Debug, Clone, Fail)]
pub enum ParseError {
    #[fail(display = "A size of zero is not valid")] ZeroSize,

    #[fail(display = "Unable decode data as utf8: {}", _0)] Utf8Error(#[cause] str::Utf8Error),

    #[fail(display = "Text of size {} is too large", _0)] TextTooLarge(i32),

    #[fail(display = "Insufficient data. Expected {} bytes, but only {} left", _0, _1)]
    InsufficientData(i32, i32),

    #[fail(display = "Did not expect a property of: {}", _0)] UnexpectedProperty(String),

    #[fail(display = "Crc mismatch. Expected {} but received {}", _0, _1)] CrcMismatch(u32, u32),

    #[fail(display = "list of size {} is too large", _0)] ListTooLarge(usize),
}

impl From<str::Utf8Error> for ParseError {
    fn from(error: str::Utf8Error) -> Self {
        ParseError::Utf8Error(error)
    }
}

#[derive(PartialEq, Debug, Clone, Fail)]
pub enum AttributeError {
    #[fail(display = "Not enough data to decode attribute {}", _0)] NotEnoughDataFor(&'static str),

    #[fail(display = "Unrecognized remote id of {}", _0)] UnrecognizedRemoteId(u8),

    #[fail(display = "Does not have an attribute implementation")] Unimplemented,

    #[fail(display = "Unexpected size for string: {}", _0)] TooBigString(i32),
}

#[derive(PartialEq, Debug, Clone, Fail)]
pub enum NetworkError {
    #[fail(display = "Not enough data to decode {}", _0)]
    NotEnoughDataFor(&'static str),

    #[fail(display = "Time is out of range: {}", _0)]
    TimeOutOfRange(f32),

    #[fail(display = "Backtracking from frame: {} to {}, the last actor ({}) had a stream id of {}. This may mean that a new update of Rocket League updated this attribute. Decoded into {:?}", _0, _1, _2, _3, _4)]
    TimeOutOfRangeUpdate(usize, usize, ActorId, StreamId, Attribute),

    #[fail(display = "Delta is out of range: {}", _0)]
    DeltaOutOfRange(f32),

    #[fail(display = "Too many stream ids ({}) for object id: {}", _0, _1)]
    MaxStreamIdTooLarge(i32, ObjectId),

    #[fail(display = "Number of channels exceeds maximum: {}", _0)]
    ChannelsTooLarge(i32),

    #[fail(display = "Object Id of {} exceeds range", _0)]
    ObjectIdOutOfRange(ObjectId),

    #[fail(display = "Stream id of {} references out of range object index: {}", _0, _1)]
    StreamTooLargeIndex(i32, i32),

    #[fail(display = "Replay contained object: {} but not the parent class: {}", _0, _1)]
    MissingParentClass(String, String),

    #[fail(display = "Parent id of {} for object id of {} was not recognized to have attributes", _0, _1)]
    ParentHasNoAttributes(ObjectId, ObjectId),

    #[fail(display = "Actor id: {} was not found", _0)]
    MissingActor(ActorId),

    #[fail(display = "Actor id: {} of object id: {} ({}) but no attributes found", _0, _1, _2)]
    MissingCache(ActorId, ObjectId, String),

    #[fail(display = "Actor id: {} of object id: {} ({}) but attribute cache id: {} not found in {}", _0, _1, _2, _3, _4)]
    MissingAttribute(ActorId, ObjectId, String, StreamId, String),

    #[fail(display = "Actor id: {} of object id: {} ({}) but attribute cache id: {} ({}) was not implemented", _0, _1, _2, _3, _4)]
    UnimplementedAttribute(ActorId, ObjectId, String, StreamId, String),

    #[fail(display = "Attribute error: {}", _0)]
    AttributeError(#[cause] AttributeError),
}
<commit_msg>Normalize error text to reference stream id<commit_after>use std::str;
use network::{ActorId, ObjectId, StreamId};
use attributes::Attribute;

#[derive(PartialEq, Debug, Clone, Fail)]
pub enum ParseError {
    #[fail(display = "A size of zero is not valid")] ZeroSize,

    #[fail(display = "Unable decode data as utf8: {}", _0)] Utf8Error(#[cause] str::Utf8Error),

    #[fail(display = "Text of size {} is too large", _0)] TextTooLarge(i32),

    #[fail(display = "Insufficient data. Expected {} bytes, but only {} left", _0, _1)]
    InsufficientData(i32, i32),

    #[fail(display = "Did not expect a property of: {}", _0)] UnexpectedProperty(String),

    #[fail(display = "Crc mismatch. Expected {} but received {}", _0, _1)] CrcMismatch(u32, u32),

    #[fail(display = "list of size {} is too large", _0)] ListTooLarge(usize),
}

impl From<str::Utf8Error> for ParseError {
    fn from(error: str::Utf8Error) -> Self {
        ParseError::Utf8Error(error)
    }
}

#[derive(PartialEq, Debug, Clone, Fail)]
pub enum AttributeError {
    #[fail(display = "Not enough data to decode attribute {}", _0)] NotEnoughDataFor(&'static str),

    #[fail(display = "Unrecognized remote id of {}", _0)] UnrecognizedRemoteId(u8),

    #[fail(display = "Does not have an attribute implementation")] Unimplemented,

    #[fail(display = "Unexpected size for string: {}", _0)] TooBigString(i32),
}

#[derive(PartialEq, Debug, Clone, Fail)]
pub enum NetworkError {
    #[fail(display = "Not enough data to decode {}", _0)]
    NotEnoughDataFor(&'static str),

    #[fail(display = "Time is out of range: {}", _0)]
    TimeOutOfRange(f32),

    #[fail(display = "Backtracking from frame: {} to {}, the last actor ({}) had a stream id of {}. This may mean that a new update of Rocket League updated this attribute. Decoded into {:?}", _0, _1, _2, _3, _4)]
    TimeOutOfRangeUpdate(usize, usize, ActorId, StreamId, Attribute),

    #[fail(display = "Delta is out of range: {}", _0)]
    DeltaOutOfRange(f32),

    #[fail(display = "Too many stream ids ({}) for object id: {}", _0, _1)]
    MaxStreamIdTooLarge(i32, ObjectId),

    #[fail(display = "Number of channels exceeds maximum: {}", _0)]
    ChannelsTooLarge(i32),

    #[fail(display = "Object Id of {} exceeds range", _0)]
    ObjectIdOutOfRange(ObjectId),

    #[fail(display = "Stream id of {} references out of range object index: {}", _0, _1)]
    StreamTooLargeIndex(i32, i32),

    #[fail(display = "Replay contained object: {} but not the parent class: {}", _0, _1)]
    MissingParentClass(String, String),

    #[fail(display = "Parent id of {} for object id of {} was not recognized to have attributes", _0, _1)]
    ParentHasNoAttributes(ObjectId, ObjectId),

    #[fail(display = "Actor id: {} was not found", _0)]
    MissingActor(ActorId),

    #[fail(display = "Actor id: {} of object id: {} ({}) but no attributes found", _0, _1, _2)]
    MissingCache(ActorId, ObjectId, String),

    #[fail(display = "Actor id: {} of object id: {} ({}) but stream id: {} not found in {}", _0, _1, _2, _3, _4)]
    MissingAttribute(ActorId, ObjectId, String, StreamId, String),

    #[fail(display = "Actor id: {} of object id: {} ({}) but stream id: {} ({}) was not implemented", _0, _1, _2, _3, _4)]
    UnimplementedAttribute(ActorId, ObjectId, String, StreamId, String),

    #[fail(display = "Attribute error: {}", _0)]
    AttributeError(#[cause] AttributeError),
}
<|endoftext|>


<commit_before>//! Implements the extraction of the principal variation from the
//! transposition table.

use std::cmp::min;
use moves::Move;
use value::*;
use depth::*;
use hash_table::*;
use search_node::SearchNode;

/// A sequence of moves from some starting position, together with the
/// value assigned to the final position.
pub struct Variation {
    /// A sequence of moves from some starting position.
    pub moves: Vec<Move>,

    /// The value assigned to the final position.
    ///
    /// The value is from the point of view of player that has the
    /// move in the starting position.
    pub value: Value,

    /// The accuracy of the assigned value.
    pub bound: BoundType,
}

/// Extracts the principal variation for a given position from the
/// transposition table.
///
/// The principal variation (PV) is the sequence of moves that the
/// engine considers best and therefore expects to be played.
pub fn extract_pv<T: HashTable, N: SearchNode>(tt: &T, position: &N) -> Variation {
    let mut p = position.clone();
    let mut our_turn = true;
    let mut moves = Vec::with_capacity(32);
    let mut root_value = VALUE_UNKNOWN;
    let mut value = VALUE_MAX;
    let mut bound = BOUND_UPPER;
    let mut depth = DEPTH_MAX + 1;

    'move_extraction: while let Some(e) = tt.probe(p.hash()) {
        depth = min(depth - 1, e.depth());

        if e.bound() == BOUND_EXACT || root_value == VALUE_UNKNOWN && e.bound() != BOUND_NONE {
            // In half of the cases the value is from other side's perspective.
            if our_turn {
                value = e.value();
                bound = e.bound();
            } else {
                value = -e.value();
                bound = match e.bound() {
                    BOUND_UPPER => BOUND_LOWER,
                    BOUND_LOWER => BOUND_UPPER,
                    b => b,
                };
            }
            assert!(value != VALUE_UNKNOWN);

            // Set the root value on the first iteration.
            if root_value == VALUE_UNKNOWN {
                root_value = value;
            }

            // Consider adding current entry's hash move to the
            // PV. There are 3 conditions for this:
            //
            // 1) The depth limit has not been reached yet.
            // 2) The value is either exact or a lower bound. This
            //    ensures that the move is either best move or a
            //    refutation move.
            // 3) The value has not diverged from the root value.
            if depth > 0 && bound & BOUND_LOWER != 0 &&
               match root_value {
                v if v < VALUE_EVAL_MIN => v as isize == value as isize + moves.len() as isize,
                v if v > VALUE_EVAL_MAX => v as isize == value as isize - moves.len() as isize,
                v => v == value,
            } {
                // Verify that the hash move is legal.
                if let Some(m) = p.try_move_digest(e.move_digest()) {
                    if p.do_move(m) {
                        moves.push(m);
                        if bound == BOUND_EXACT {
                            our_turn = !our_turn;
                            continue 'move_extraction;
                        }
                    }
                }
            }
        }
        break 'move_extraction;
    }

    Variation {
        value: if root_value != VALUE_UNKNOWN {
            root_value
        } else {
            value
        },
        bound: bound,
        moves: moves,
    }
}
<commit_msg>fix subtle bug in extract_pv()<commit_after>//! Implements the extraction of the principal variation from the
//! transposition table.

use std::cmp::min;
use moves::Move;
use value::*;
use depth::*;
use hash_table::*;
use search_node::SearchNode;

/// A sequence of moves from some starting position, together with the
/// value assigned to the final position.
pub struct Variation {
    /// A sequence of moves from some starting position.
    pub moves: Vec<Move>,

    /// The value assigned to the final position.
    ///
    /// The value is from the point of view of player that has the
    /// move in the starting position.
    pub value: Value,

    /// The accuracy of the assigned value.
    pub bound: BoundType,
}

/// Extracts the principal variation for a given position from the
/// transposition table.
///
/// The principal variation (PV) is the sequence of moves that the
/// engine considers best and therefore expects to be played.
pub fn extract_pv<T: HashTable, N: SearchNode>(tt: &T, position: &N) -> Variation {
    let mut p = position.clone();
    let mut our_turn = true;
    let mut moves = Vec::with_capacity(32);
    let mut root_value = VALUE_UNKNOWN;
    let mut value = VALUE_MAX;
    let mut bound = BOUND_UPPER;
    let mut depth = DEPTH_MAX + 1;

    'move_extraction: while let Some(e) = tt.probe(p.hash()) {
        depth = min(depth - 1, e.depth());

        if e.bound() == BOUND_EXACT || root_value == VALUE_UNKNOWN && e.bound() != BOUND_NONE {
            // In half of the cases the value is from other side's perspective.
            if our_turn {
                value = e.value();
                bound = e.bound();
            } else {
                value = -e.value();
                bound = match e.bound() {
                    BOUND_UPPER => BOUND_LOWER,
                    BOUND_LOWER => BOUND_UPPER,
                    b => b,
                };
            }
            assert!(value != VALUE_UNKNOWN);

            // Set the root value on the first iteration.
            if root_value == VALUE_UNKNOWN {
                root_value = value;
            }

            // Consider adding current entry's hash move to the
            // PV. There are 3 conditions for this:
            //
            // 1) The depth limit has not been reached yet.
            // 2) The move is either best move or a refutation move.
            // 3) The value has not diverged from the root value.
            if depth > 0 && e.bound() & BOUND_LOWER != 0 &&
               match root_value {
                v if v < VALUE_EVAL_MIN => v as isize == value as isize + moves.len() as isize,
                v if v > VALUE_EVAL_MAX => v as isize == value as isize - moves.len() as isize,
                v => v == value,
            } {
                // Verify that the hash move is legal.
                if let Some(m) = p.try_move_digest(e.move_digest()) {
                    if p.do_move(m) {
                        moves.push(m);
                        if e.bound() == BOUND_EXACT {
                            our_turn = !our_turn;
                            continue 'move_extraction;
                        }
                    }
                }
            }
        }
        break 'move_extraction;
    }

    Variation {
        value: if root_value != VALUE_UNKNOWN {
            root_value
        } else {
            value
        },
        bound: bound,
        moves: moves,
    }
}
<|endoftext|>


<commit_before>//! A SCAllocator that can allocate fixed size objects.

use crate::*;

/// A genius(?) const min()
///
/// # What this does
/// * create an array of the two elements you want to choose between
/// * create an arbitrary boolean expression
/// * cast said expresison to a usize
/// * use that value to index into the array created above
///
/// # Source
/// https://stackoverflow.com/questions/53619695/calculating-maximum-value-of-a-set-of-constant-expressions-at-compile-time
#[cfg(feature = "unstable")]
const fn cmin(a: usize, b: usize) -> usize {
    [a, b][(a > b) as usize]
}

/// The boring variant of min (not const).
#[cfg(not(feature = "unstable"))]
fn cmin(a: usize, b: usize) -> usize {
    core::cmp::min(a, b)
}

/// A slab allocator allocates elements of a fixed size.
///
/// It maintains three internal lists of objects that implement `AllocablePage`
/// from which it can allocate memory.
///
///  * `empty_slabs`: Is a list of pages that the SCAllocator maintains, but
///    has 0 allocations in them, these can be given back to a requestor in case
///    of reclamation.
///  * `slabs`: A list of pages partially allocated and still have room for more.
///  * `full_slabs`: A list of pages that are completely allocated.
///
/// On allocation we allocate memory from `slabs`, however if the list is empty
/// we try to reclaim a page from `empty_slabs` before we return with an out-of-memory
/// error. If a page becomes full after the allocation we move it from `slabs` to
/// `full_slabs`.
///
/// Similarly, on dealloaction we might move a page from `full_slabs` to `slabs`
/// or from `slabs` to `empty_slabs` after we deallocated an object.
///
/// If an allocation returns `OutOfMemory` a client using SCAllocator can refill
/// it using the `refill` function.
pub struct SCAllocator<'a, P: AllocablePage> {
    /// Maximum possible allocation size for this `SCAllocator`.
    pub(crate) size: usize,
    /// Keeps track of succeeded allocations.
    pub(crate) allocation_count: usize,
    /// max objects per page
    pub(crate) obj_per_page: usize,
    /// List of empty ObjectPages (nothing allocated in these).
    pub(crate) empty_slabs: PageList<'a, P>,
    /// List of partially used ObjectPage (some objects allocated but pages are not full).
    pub(crate) slabs: PageList<'a, P>,
    /// List of full ObjectPages (everything allocated in these don't need to search them).
    pub(crate) full_slabs: PageList<'a, P>,
}

/// Creates an instance of a scallocator, we do this in a macro because we
/// re-use the code in const and non-const functions
macro_rules! new_sc_allocator {
    ($size:expr) => {
        SCAllocator {
            size: $size,
            allocation_count: 0,
            obj_per_page: cmin((P::SIZE - 80) / $size, 8 * 64),
            empty_slabs: PageList::new(),
            slabs: PageList::new(),
            full_slabs: PageList::new(),
        }
    };
}

impl<'a, P: AllocablePage> SCAllocator<'a, P> {
    const REBALANCE_COUNT: usize = 10_000;

    /// Create a new SCAllocator.
    #[cfg(feature = "unstable")]
    pub const fn new(size: usize) -> SCAllocator<'a, P> {
        new_sc_allocator!(size)
    }

    #[cfg(not(feature = "unstable"))]
    pub fn new(size: usize) -> SCAllocator<'a, P> {
        new_sc_allocator!(size)
    }

    /// Returns the maximum supported object size of this allocator.
    pub fn size(&self) -> usize {
        self.size
    }

    /// Add a new ObjectPage.
    fn insert_partial_slab(&mut self, new_head: &'a mut P) {
        self.slabs.insert_front(new_head);
    }

    /// Add page to empty list.
    fn insert_empty(&mut self, new_head: &'a mut P) {
        assert_eq!(
            new_head as *const P as usize % P::SIZE,
            0,
            "Inserted page is not aligned to page-size."
        );
        self.empty_slabs.insert_front(new_head);
    }

    /// Since `dealloc` can not reassign pages without requiring a lock
    /// we check slabs and full slabs periodically as part of `alloc`
    /// and move them to the empty or partially allocated slab lists.
    pub(crate) fn check_page_assignments(&mut self) {
        for slab_page in self.full_slabs.iter_mut() {
            if !slab_page.is_full() {
                // We need to move it from self.full_slabs -> self.slabs
                trace!("move {:p} full -> partial", slab_page);
                self.move_full_to_partial(slab_page);
            }
        }

        for slab_page in self.slabs.iter_mut() {
            if slab_page.is_empty(self.obj_per_page) {
                // We need to move it from self.slabs -> self.empty_slabs
                trace!("move {:p} partial -> empty", slab_page);
                self.move_to_empty(slab_page);
            }
        }
    }

    /// Move a page from `slabs` to `empty_slabs`.
    fn move_to_empty(&mut self, page: &'a mut P) {
        let page_ptr = page as *const P;

        debug_assert!(self.slabs.contains(page_ptr));
        debug_assert!(
            !self.empty_slabs.contains(page_ptr),
            "Page {:p} already in emtpy_slabs",
            page_ptr
        );

        self.slabs.remove_from_list(page);
        self.empty_slabs.insert_front(page);

        debug_assert!(!self.slabs.contains(page_ptr));
        debug_assert!(self.empty_slabs.contains(page_ptr));
    }

    /// Move a page from `full_slabs` to `slab`.
    fn move_partial_to_full(&mut self, page: &'a mut P) {
        let page_ptr = page as *const P;

        debug_assert!(self.slabs.contains(page_ptr));
        debug_assert!(!self.full_slabs.contains(page_ptr));

        self.slabs.remove_from_list(page);
        self.full_slabs.insert_front(page);

        debug_assert!(!self.slabs.contains(page_ptr));
        debug_assert!(self.full_slabs.contains(page_ptr));
    }

    /// Move a page from `full_slabs` to `slab`.
    fn move_full_to_partial(&mut self, page: &'a mut P) {
        let page_ptr = page as *const P;

        debug_assert!(!self.slabs.contains(page_ptr));
        debug_assert!(self.full_slabs.contains(page_ptr));

        self.full_slabs.remove_from_list(page);
        self.slabs.insert_front(page);

        debug_assert!(self.slabs.contains(page_ptr));
        debug_assert!(!self.full_slabs.contains(page_ptr));
    }

    /// Tries to allocate a block of memory with respect to the `layout`.
    /// Searches within already allocated slab pages, if no suitable spot is found
    /// will try to use a page from the empty page list.
    ///
    /// # Arguments
    ///  * `sc_layout`: This is not the original layout but adjusted for the
    ///     SCAllocator size (>= original).
    fn try_allocate_from_pagelist(&mut self, sc_layout: Layout) -> *mut u8 {
        // TODO: Do we really need to check multiple slab pages (due to alignment)
        // If not we can get away with a singly-linked list and have 8 more bytes
        // for the bitfield in an ObjectPage.

        for slab_page in self.slabs.iter_mut() {
            let ptr = slab_page.allocate(sc_layout);
            if !ptr.is_null() {
                if slab_page.is_full() {
                    trace!("move {:p} partial -> full", slab_page);
                    self.move_partial_to_full(slab_page);
                }
                self.allocation_count += 1;
                return ptr;
            } else {
                continue;
            }
        }

        // Periodically rebalance page-lists (since dealloc can't do it for us)
        if self.allocation_count % SCAllocator::<P>::REBALANCE_COUNT == 0 {
            self.check_page_assignments();
        }

        ptr::null_mut()
    }

    /// Refill the SCAllocator
    ///
    /// # Safety
    /// ObjectPage needs to be empty etc.
    pub unsafe fn refill(&mut self, page: &'a mut P) {
        page.bitfield_mut().initialize(self.size, P::SIZE - 80);
        *page.prev() = Rawlink::none();
        *page.next() = Rawlink::none();
        trace!("adding page to SCAllocator {:p}", page);
        self.insert_empty(page);
    }

    /// Allocates a block of memory descriped by `layout`.
    ///
    /// Returns a pointer to a valid region of memory or an
    /// AllocationError.
    ///
    /// The function may also move around pages between lists
    /// (empty -> partial or partial -> full).
    pub fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocationError> {
        trace!(
            "SCAllocator({}) is trying to allocate {:?}",
            self.size,
            layout
        );
        assert!(layout.size() <= self.size);
        assert!(self.size <= (P::SIZE - CACHE_LINE_SIZE));
        let new_layout = unsafe { Layout::from_size_align_unchecked(self.size, layout.align()) };
        assert!(new_layout.size() >= layout.size());

        let ptr = {
            // Try to allocate from partial slabs,
            // if we fail check if we have empty pages and allocate from there
            let ptr = self.try_allocate_from_pagelist(new_layout);
            if ptr.is_null() && self.empty_slabs.head.is_some() {
                // Re-try allocation in empty page
                let empty_page = self.empty_slabs.pop().expect("We checked head.is_some()");
                debug_assert!(!self.empty_slabs.contains(empty_page));

                let ptr = empty_page.allocate(layout);
                debug_assert!(!ptr.is_null(), "Allocation must have succeeded here.");

                trace!(
                    "move {:p} empty -> partial empty count {}",
                    empty_page,
                    self.empty_slabs.elements
                );
                // Move empty page to partial pages
                self.insert_partial_slab(empty_page);
                ptr
            } else {
                ptr
            }
        };

        let res = NonNull::new(ptr).ok_or(AllocationError::OutOfMemory);

        if !ptr.is_null() {
            trace!(
                "SCAllocator({}) allocated ptr=0x{:x}",
                self.size,
                ptr as usize
            );
        }

        res
    }

    /// Deallocates a previously allocated `ptr` described by `Layout`.
    ///
    /// May return an error in case an invalid `layout` is provided.
    /// The function may also move internal slab pages between lists partial -> empty
    /// or full -> partial lists.
    pub fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) -> Result<(), AllocationError> {
        assert!(layout.size() <= self.size);
        assert!(self.size <= (P::SIZE - CACHE_LINE_SIZE));
        trace!(
            "SCAllocator({}) is trying to deallocate ptr = {:p} layout={:?} P.size= {}",
            self.size,
            ptr,
            layout,
            P::SIZE
        );

        let page = (ptr.as_ptr() as usize) & !(P::SIZE - 1) as usize;

        // Figure out which page we are on and construct a reference to it
        // TODO: The linked list will have another &mut reference
        let slab_page = unsafe { mem::transmute::<VAddr, &'a mut P>(page) };
        let new_layout = unsafe { Layout::from_size_align_unchecked(self.size, layout.align()) };

        let ret = slab_page.deallocate(ptr, new_layout);
        debug_assert!(ret.is_ok(), "Slab page deallocate won't fail at the moment");
        ret
    }
}
<commit_msg>Decrease the rebalance trigger.<commit_after>//! A SCAllocator that can allocate fixed size objects.

use crate::*;

/// A genius(?) const min()
///
/// # What this does
/// * create an array of the two elements you want to choose between
/// * create an arbitrary boolean expression
/// * cast said expresison to a usize
/// * use that value to index into the array created above
///
/// # Source
/// https://stackoverflow.com/questions/53619695/calculating-maximum-value-of-a-set-of-constant-expressions-at-compile-time
#[cfg(feature = "unstable")]
const fn cmin(a: usize, b: usize) -> usize {
    [a, b][(a > b) as usize]
}

/// The boring variant of min (not const).
#[cfg(not(feature = "unstable"))]
fn cmin(a: usize, b: usize) -> usize {
    core::cmp::min(a, b)
}

/// A slab allocator allocates elements of a fixed size.
///
/// It maintains three internal lists of objects that implement `AllocablePage`
/// from which it can allocate memory.
///
///  * `empty_slabs`: Is a list of pages that the SCAllocator maintains, but
///    has 0 allocations in them, these can be given back to a requestor in case
///    of reclamation.
///  * `slabs`: A list of pages partially allocated and still have room for more.
///  * `full_slabs`: A list of pages that are completely allocated.
///
/// On allocation we allocate memory from `slabs`, however if the list is empty
/// we try to reclaim a page from `empty_slabs` before we return with an out-of-memory
/// error. If a page becomes full after the allocation we move it from `slabs` to
/// `full_slabs`.
///
/// Similarly, on dealloaction we might move a page from `full_slabs` to `slabs`
/// or from `slabs` to `empty_slabs` after we deallocated an object.
///
/// If an allocation returns `OutOfMemory` a client using SCAllocator can refill
/// it using the `refill` function.
pub struct SCAllocator<'a, P: AllocablePage> {
    /// Maximum possible allocation size for this `SCAllocator`.
    pub(crate) size: usize,
    /// Keeps track of succeeded allocations.
    pub(crate) allocation_count: usize,
    /// max objects per page
    pub(crate) obj_per_page: usize,
    /// List of empty ObjectPages (nothing allocated in these).
    pub(crate) empty_slabs: PageList<'a, P>,
    /// List of partially used ObjectPage (some objects allocated but pages are not full).
    pub(crate) slabs: PageList<'a, P>,
    /// List of full ObjectPages (everything allocated in these don't need to search them).
    pub(crate) full_slabs: PageList<'a, P>,
}

/// Creates an instance of a scallocator, we do this in a macro because we
/// re-use the code in const and non-const functions
macro_rules! new_sc_allocator {
    ($size:expr) => {
        SCAllocator {
            size: $size,
            allocation_count: 0,
            obj_per_page: cmin((P::SIZE - 80) / $size, 8 * 64),
            empty_slabs: PageList::new(),
            slabs: PageList::new(),
            full_slabs: PageList::new(),
        }
    };
}

impl<'a, P: AllocablePage> SCAllocator<'a, P> {
    const REBALANCE_COUNT: usize = 64;

    /// Create a new SCAllocator.
    #[cfg(feature = "unstable")]
    pub const fn new(size: usize) -> SCAllocator<'a, P> {
        new_sc_allocator!(size)
    }

    #[cfg(not(feature = "unstable"))]
    pub fn new(size: usize) -> SCAllocator<'a, P> {
        new_sc_allocator!(size)
    }

    /// Returns the maximum supported object size of this allocator.
    pub fn size(&self) -> usize {
        self.size
    }

    /// Add a new ObjectPage.
    fn insert_partial_slab(&mut self, new_head: &'a mut P) {
        self.slabs.insert_front(new_head);
    }

    /// Add page to empty list.
    fn insert_empty(&mut self, new_head: &'a mut P) {
        assert_eq!(
            new_head as *const P as usize % P::SIZE,
            0,
            "Inserted page is not aligned to page-size."
        );
        self.empty_slabs.insert_front(new_head);
    }

    /// Since `dealloc` can not reassign pages without requiring a lock
    /// we check slabs and full slabs periodically as part of `alloc`
    /// and move them to the empty or partially allocated slab lists.
    pub(crate) fn check_page_assignments(&mut self) {
        for slab_page in self.full_slabs.iter_mut() {
            if !slab_page.is_full() {
                // We need to move it from self.full_slabs -> self.slabs
                trace!("move {:p} full -> partial", slab_page);
                self.move_full_to_partial(slab_page);
            }
        }

        for slab_page in self.slabs.iter_mut() {
            if slab_page.is_empty(self.obj_per_page) {
                // We need to move it from self.slabs -> self.empty_slabs
                trace!("move {:p} partial -> empty", slab_page);
                self.move_to_empty(slab_page);
            }
        }
    }

    /// Move a page from `slabs` to `empty_slabs`.
    fn move_to_empty(&mut self, page: &'a mut P) {
        let page_ptr = page as *const P;

        debug_assert!(self.slabs.contains(page_ptr));
        debug_assert!(
            !self.empty_slabs.contains(page_ptr),
            "Page {:p} already in emtpy_slabs",
            page_ptr
        );

        self.slabs.remove_from_list(page);
        self.empty_slabs.insert_front(page);

        debug_assert!(!self.slabs.contains(page_ptr));
        debug_assert!(self.empty_slabs.contains(page_ptr));
    }

    /// Move a page from `full_slabs` to `slab`.
    fn move_partial_to_full(&mut self, page: &'a mut P) {
        let page_ptr = page as *const P;

        debug_assert!(self.slabs.contains(page_ptr));
        debug_assert!(!self.full_slabs.contains(page_ptr));

        self.slabs.remove_from_list(page);
        self.full_slabs.insert_front(page);

        debug_assert!(!self.slabs.contains(page_ptr));
        debug_assert!(self.full_slabs.contains(page_ptr));
    }

    /// Move a page from `full_slabs` to `slab`.
    fn move_full_to_partial(&mut self, page: &'a mut P) {
        let page_ptr = page as *const P;

        debug_assert!(!self.slabs.contains(page_ptr));
        debug_assert!(self.full_slabs.contains(page_ptr));

        self.full_slabs.remove_from_list(page);
        self.slabs.insert_front(page);

        debug_assert!(self.slabs.contains(page_ptr));
        debug_assert!(!self.full_slabs.contains(page_ptr));
    }

    /// Tries to allocate a block of memory with respect to the `layout`.
    /// Searches within already allocated slab pages, if no suitable spot is found
    /// will try to use a page from the empty page list.
    ///
    /// # Arguments
    ///  * `sc_layout`: This is not the original layout but adjusted for the
    ///     SCAllocator size (>= original).
    fn try_allocate_from_pagelist(&mut self, sc_layout: Layout) -> *mut u8 {
        // TODO: Do we really need to check multiple slab pages (due to alignment)
        // If not we can get away with a singly-linked list and have 8 more bytes
        // for the bitfield in an ObjectPage.

        for slab_page in self.slabs.iter_mut() {
            let ptr = slab_page.allocate(sc_layout);
            if !ptr.is_null() {
                if slab_page.is_full() {
                    trace!("move {:p} partial -> full", slab_page);
                    self.move_partial_to_full(slab_page);
                }
                self.allocation_count += 1;
                return ptr;
            } else {
                continue;
            }
        }

        // Periodically rebalance page-lists (since dealloc can't do it for us)
        if self.allocation_count % SCAllocator::<P>::REBALANCE_COUNT == 0 {
            self.check_page_assignments();
        }

        ptr::null_mut()
    }

    /// Refill the SCAllocator
    ///
    /// # Safety
    /// ObjectPage needs to be empty etc.
    pub unsafe fn refill(&mut self, page: &'a mut P) {
        page.bitfield_mut().initialize(self.size, P::SIZE - 80);
        *page.prev() = Rawlink::none();
        *page.next() = Rawlink::none();
        trace!("adding page to SCAllocator {:p}", page);
        self.insert_empty(page);
    }

    /// Allocates a block of memory descriped by `layout`.
    ///
    /// Returns a pointer to a valid region of memory or an
    /// AllocationError.
    ///
    /// The function may also move around pages between lists
    /// (empty -> partial or partial -> full).
    pub fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocationError> {
        trace!(
            "SCAllocator({}) is trying to allocate {:?}",
            self.size,
            layout
        );
        assert!(layout.size() <= self.size);
        assert!(self.size <= (P::SIZE - CACHE_LINE_SIZE));
        let new_layout = unsafe { Layout::from_size_align_unchecked(self.size, layout.align()) };
        assert!(new_layout.size() >= layout.size());

        let ptr = {
            // Try to allocate from partial slabs,
            // if we fail check if we have empty pages and allocate from there
            let ptr = self.try_allocate_from_pagelist(new_layout);
            if ptr.is_null() && self.empty_slabs.head.is_some() {
                // Re-try allocation in empty page
                let empty_page = self.empty_slabs.pop().expect("We checked head.is_some()");
                debug_assert!(!self.empty_slabs.contains(empty_page));

                let ptr = empty_page.allocate(layout);
                debug_assert!(!ptr.is_null(), "Allocation must have succeeded here.");

                trace!(
                    "move {:p} empty -> partial empty count {}",
                    empty_page,
                    self.empty_slabs.elements
                );
                // Move empty page to partial pages
                self.insert_partial_slab(empty_page);
                ptr
            } else {
                ptr
            }
        };

        let res = NonNull::new(ptr).ok_or(AllocationError::OutOfMemory);

        if !ptr.is_null() {
            trace!(
                "SCAllocator({}) allocated ptr=0x{:x}",
                self.size,
                ptr as usize
            );
        }

        res
    }

    /// Deallocates a previously allocated `ptr` described by `Layout`.
    ///
    /// May return an error in case an invalid `layout` is provided.
    /// The function may also move internal slab pages between lists partial -> empty
    /// or full -> partial lists.
    pub fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) -> Result<(), AllocationError> {
        assert!(layout.size() <= self.size);
        assert!(self.size <= (P::SIZE - CACHE_LINE_SIZE));
        trace!(
            "SCAllocator({}) is trying to deallocate ptr = {:p} layout={:?} P.size= {}",
            self.size,
            ptr,
            layout,
            P::SIZE
        );

        let page = (ptr.as_ptr() as usize) & !(P::SIZE - 1) as usize;

        // Figure out which page we are on and construct a reference to it
        // TODO: The linked list will have another &mut reference
        let slab_page = unsafe { mem::transmute::<VAddr, &'a mut P>(page) };
        let new_layout = unsafe { Layout::from_size_align_unchecked(self.size, layout.align()) };

        let ret = slab_page.deallocate(ptr, new_layout);
        debug_assert!(ret.is_ok(), "Slab page deallocate won't fail at the moment");
        ret
    }
}
<|endoftext|>


<commit_before>use ndarray::Array1;
use geometry::Space;

/// An interface for dealing with functions that may be evaluated.
pub trait Function<I: ?Sized, O> {
    /// Evaluates the function and returns its output.
    fn evaluate(&self, input: &I) -> O;
}

impl<I: ?Sized, O, T> Function<I, O> for Box<T>
    where T: Function<I, O>
{
    fn evaluate(&self, input: &I) -> O {
        (**self).evaluate(input)
    }
}

/// An interface for dealing with adaptive functions.
pub trait Parameterised<I: ?Sized, E> {
    fn update(&mut self, input: &I, error: E);
}

impl<I: ?Sized, E, T> Parameterised<I, E> for Box<T>
    where T: Parameterised<I, E>
{
    fn update(&mut self, input: &I, error: E) {
        (**self).update(input, error)
    }
}

/// An interface for linearly parameterised functions.
pub trait Linear<S: Space> {
    fn phi(&self, input: &S::Repr) -> Array1<f64>;
}

/// An interface for value functions.
pub trait VFunction<S: Space>:
    Function<S::Repr, f64> + Parameterised<S::Repr, f64>
{
    fn evaluate_phi(&self, _: &Array1<f64>) -> f64 {
        unimplemented!()
    }

    fn update_phi(&mut self, _: &Array1<f64>, _: f64) {
        unimplemented!()
    }
}

/// An interface for action-value functions.
pub trait QFunction<S: Space>:
    Function<S::Repr, Vec<f64>> + Parameterised<S::Repr, Vec<f64>>
{
    fn evaluate_action(&self, input: &S::Repr, action: usize) -> f64;
    fn update_action(&mut self, input: &S::Repr, action: usize, error: f64);

    fn evaluate_phi(&self, _: &Array1<f64>) -> Vec<f64> {
        unimplemented!();
    }

    fn evaluate_action_phi(&self, _: &Array1<f64>, _: usize) -> f64 {
        unimplemented!();
    }

    fn update_phi(&mut self, _: &Array1<f64>, _: Vec<f64>) {
        unimplemented!();
    }

    fn update_action_phi(&mut self, _: &Array1<f64>, _: usize, _: f64) {
        unimplemented!();
    }

    // NOTE: Eventually we may want to distinguish between the feature vector
    //       for each action. I can't see this being needed anytime soon so
    //       for now we will just have a single entry point for computing phi.
    // fn phi(&self, input: &S::Repr) -> Array2<f64> {
        // unimplemented!()
    // }

    // fn phi_action(&self, input: &S::Repr, _: usize) -> Array1<f64>{
        // unimplemented!()
    // }

    // fn evaluate_phi(&self, phi: &Array2<f64>) -> f64 {
        // unimplemented!();
    // }

    // fn evaluate_action_phi(&self, phi: &Array1<f64>, action: usize) -> f64 {
        // unimplemented!();
    // }

    // fn update_phi(&mut self, phi: &Array2<f64>, error: f64) -> f64 {
        // unimplemented!();
    // }

    // fn update_action_phi(&mut self, phi: &Array1<f64>, action: usize, error: f64) -> f64 {
        // unimplemented!();
    // }
}

// XXX: Bug with Rust that renders ICE errors when using VFunctionGroup.
mod fgroup;
pub use self::fgroup::VFunctionGroup;

mod table;
mod partitions;
mod rbf_network;
mod basis_network;
// mod leaf;
// mod sutton_tiles;

pub mod exact {
    pub use fa::table::*;
}

pub mod linear {
    pub use fa::partitions::*;
    pub use fa::rbf_network::*;
    pub use fa::basis_network::*;
    // pub use fa::sutton_tiles::*;
}

pub mod non_linear {
    // pub use fa::leaf::*;
}
<commit_msg>Added Box wrapper impl blocks to fa traits<commit_after>use ndarray::Array1;
use geometry::Space;

/// An interface for dealing with functions that may be evaluated.
pub trait Function<I: ?Sized, O> {
    /// Evaluates the function and returns its output.
    fn evaluate(&self, input: &I) -> O;
}

impl<I: ?Sized, O, T> Function<I, O> for Box<T>
    where T: Function<I, O>
{
    fn evaluate(&self, input: &I) -> O {
        (**self).evaluate(input)
    }
}

/// An interface for dealing with adaptive functions.
pub trait Parameterised<I: ?Sized, E> {
    fn update(&mut self, input: &I, error: E);
}

impl<I: ?Sized, E, T> Parameterised<I, E> for Box<T>
    where T: Parameterised<I, E>
{
    fn update(&mut self, input: &I, error: E) {
        (**self).update(input, error)
    }
}

/// An interface for linearly parameterised functions.
pub trait Linear<S: Space> {
    fn phi(&self, input: &S::Repr) -> Array1<f64>;
}

/// An interface for value functions.
pub trait VFunction<S: Space>:
    Function<S::Repr, f64> + Parameterised<S::Repr, f64>
{
    fn evaluate_phi(&self, _: &Array1<f64>) -> f64 {
        unimplemented!()
    }

    fn update_phi(&mut self, _: &Array1<f64>, _: f64) {
        unimplemented!()
    }
}

impl<S: Space, T> VFunction<S> for Box<T>
    where T: VFunction<S>
{
    fn evaluate_phi(&self, phi: &Array1<f64>) -> f64 {
        (**self).evaluate_phi(phi)
    }

    fn update_phi(&mut self, phi: &Array1<f64>, error: f64) {
        (**self).update_phi(phi, error);
    }
}

/// An interface for action-value functions.
pub trait QFunction<S: Space>:
    Function<S::Repr, Vec<f64>> + Parameterised<S::Repr, Vec<f64>>
{
    fn evaluate_action(&self, input: &S::Repr, action: usize) -> f64;
    fn update_action(&mut self, input: &S::Repr, action: usize, error: f64);

    fn evaluate_phi(&self, _: &Array1<f64>) -> Vec<f64> {
        unimplemented!();
    }

    fn evaluate_action_phi(&self, _: &Array1<f64>, _: usize) -> f64 {
        unimplemented!();
    }

    fn update_phi(&mut self, _: &Array1<f64>, _: Vec<f64>) {
        unimplemented!();
    }

    fn update_action_phi(&mut self, _: &Array1<f64>, _: usize, _: f64) {
        unimplemented!();
    }

    // NOTE: Eventually we may want to distinguish between the feature vector
    //       for each action. I can't see this being needed anytime soon so
    //       for now we will just have a single entry point for computing phi.
    // fn phi(&self, input: &S::Repr) -> Array2<f64> {
        // unimplemented!()
    // }

    // fn phi_action(&self, input: &S::Repr, _: usize) -> Array1<f64>{
        // unimplemented!()
    // }

    // fn evaluate_phi(&self, phi: &Array2<f64>) -> f64 {
        // unimplemented!();
    // }

    // fn evaluate_action_phi(&self, phi: &Array1<f64>, action: usize) -> f64 {
        // unimplemented!();
    // }

    // fn update_phi(&mut self, phi: &Array2<f64>, error: f64) -> f64 {
        // unimplemented!();
    // }

    // fn update_action_phi(&mut self, phi: &Array1<f64>, action: usize, error: f64) -> f64 {
        // unimplemented!();
    // }
}

impl<S: Space, T> QFunction<S> for Box<T>
    where T: QFunction<S>
{
    fn evaluate_action(&self, input: &S::Repr, action: usize) -> f64 {
        (**self).evaluate_action(input, action)
    }

    fn update_action(&mut self, input: &S::Repr, action: usize, error: f64) {
        (**self).update_action(input, action, error);
    }

    fn evaluate_phi(&self, phi: &Array1<f64>) -> Vec<f64> {
        (**self).evaluate_phi(phi)
    }

    fn evaluate_action_phi(&self, phi: &Array1<f64>, action: usize) -> f64 {
        (**self).evaluate_action_phi(phi, action)
    }

    fn update_phi(&mut self, phi: &Array1<f64>, error: Vec<f64>) {
        (**self).update_phi(phi, error);
    }

    fn update_action_phi(&mut self, phi: &Array1<f64>, action: usize, error: f64) {
        (**self).update_action_phi(phi, action, error)
    }
}

// XXX: Bug with Rust that renders ICE errors when using VFunctionGroup.
mod fgroup;
pub use self::fgroup::VFunctionGroup;

mod table;
mod partitions;
mod rbf_network;
mod basis_network;
// mod leaf;
// mod sutton_tiles;

pub mod exact {
    pub use fa::table::*;
}

pub mod linear {
    pub use fa::partitions::*;
    pub use fa::rbf_network::*;
    pub use fa::basis_network::*;
    // pub use fa::sutton_tiles::*;
}

pub mod non_linear {
    // pub use fa::leaf::*;
}
<|endoftext|>


<commit_before>extern crate clap;

use clap::{App, Arg, SubCommand, AppSettings, ErrorKind};

#[test]
fn sub_command_negate_required() {
    App::new("sub_command_negate")
        .setting(AppSettings::SubcommandsNegateReqs)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from(vec!["myprog", "sub1"]);
}

#[test]
fn global_version() {
    let app = App::new("global_version")
        .setting(AppSettings::GlobalVersion)
        .version("1.1")
        .subcommand(SubCommand::with_name("sub1"));
    assert_eq!(app.p.subcommands[0].p.meta.version, Some("1.1"));
}

#[test]
fn sub_command_negate_required_2() {
    let result = App::new("sub_command_negate")
        .setting(AppSettings::SubcommandsNegateReqs)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingRequiredArgument);
}

#[test]
fn sub_command_required() {
    let result = App::new("sc_required")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingSubcommand);
}

#[test]
fn arg_required_else_help() {
    let result = App::new("arg_required")
        .setting(AppSettings::ArgRequiredElseHelp)
        .arg(Arg::with_name("test")
               .index(1))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingArgumentOrSubcommand);
}

#[test]
fn no_bin_name() {
    let result = App::new("arg_required")
        .setting(AppSettings::NoBinaryName)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .get_matches_from_safe(vec!["testing"]);
    assert!(result.is_ok());
    let matches = result.unwrap();
    assert_eq!(matches.value_of("test").unwrap(), "testing");
}

#[test]
fn unified_help() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .setting(AppSettings::UnifiedHelpMessage)
        .args_from_usage("-f, --flag 'some flag'
                          [arg1] 'some pos arg'
                          --option [opt] 'some option'");
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).ok().expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [OPTIONS] [ARGS]

OPTIONS:
    -f, --flag            some flag
    -h, --help            Prints help information
        --option <opt>    some option
    -V, --version         Prints version information

ARGS:
    <arg1>    some pos arg"));
}

#[test]
fn skip_possible_values() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .setting(AppSettings::HidePossibleValuesInHelp)
        .args(&[Arg::from_usage("-o, --opt [opt] 'some option'").possible_values(&["one", "two"]),
                Arg::from_usage("[arg1] 'some pos arg'").possible_values(&["three", "four"])]);
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [FLAGS] [OPTIONS] [ARGS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -o, --opt <opt>    some option

ARGS:
    <arg1>    some pos arg"));
}
<commit_msg>tests(Settings): adds tests for global subcommands<commit_after>extern crate clap;

use clap::{App, Arg, SubCommand, AppSettings, ErrorKind};

#[test]
fn sub_command_negate_required() {
    App::new("sub_command_negate")
        .setting(AppSettings::SubcommandsNegateReqs)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from(vec!["myprog", "sub1"]);
}

#[test]
fn global_version() {
    let app = App::new("global_version")
        .setting(AppSettings::GlobalVersion)
        .version("1.1")
        .subcommand(SubCommand::with_name("sub1"));
    assert_eq!(app.p.subcommands[0].p.meta.version, Some("1.1"));
}

#[test]
fn sub_command_negate_required_2() {
    let result = App::new("sub_command_negate")
        .setting(AppSettings::SubcommandsNegateReqs)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingRequiredArgument);
}

#[test]
fn sub_command_required() {
    let result = App::new("sc_required")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(SubCommand::with_name("sub1"))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingSubcommand);
}

#[test]
fn arg_required_else_help() {
    let result = App::new("arg_required")
        .setting(AppSettings::ArgRequiredElseHelp)
        .arg(Arg::with_name("test")
               .index(1))
        .get_matches_from_safe(vec![""]);
    assert!(result.is_err());
    let err = result.err().unwrap();
    assert_eq!(err.kind, ErrorKind::MissingArgumentOrSubcommand);
}

#[test]
fn no_bin_name() {
    let result = App::new("arg_required")
        .setting(AppSettings::NoBinaryName)
        .arg(Arg::with_name("test")
               .required(true)
               .index(1))
        .get_matches_from_safe(vec!["testing"]);
    assert!(result.is_ok());
    let matches = result.unwrap();
    assert_eq!(matches.value_of("test").unwrap(), "testing");
}

#[test]
fn unified_help() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .setting(AppSettings::UnifiedHelpMessage)
        .args_from_usage("-f, --flag 'some flag'
                          [arg1] 'some pos arg'
                          --option [opt] 'some option'");
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).ok().expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [OPTIONS] [ARGS]

OPTIONS:
    -f, --flag            some flag
    -h, --help            Prints help information
        --option <opt>    some option
    -V, --version         Prints version information

ARGS:
    <arg1>    some pos arg"));
}

#[test]
fn skip_possible_values() {
    let mut app = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .setting(AppSettings::HidePossibleValuesInHelp)
        .args(&[Arg::from_usage("-o, --opt [opt] 'some option'").possible_values(&["one", "two"]),
                Arg::from_usage("[arg1] 'some pos arg'").possible_values(&["three", "four"])]);
    // We call a get_matches method to cause --help and --version to be built
    let _ = app.get_matches_from_safe_borrow(vec![""]);

    // Now we check the output of print_help()
    let mut help = vec![];
    app.write_help(&mut help).expect("failed to print help");
    assert_eq!(&*String::from_utf8_lossy(&*help), &*String::from("test 1.3\n\
Kevin K.
tests stuff

USAGE:
    test [FLAGS] [OPTIONS] [ARGS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -o, --opt <opt>    some option

ARGS:
    <arg1>    some pos arg"));
}

#[test]
fn global_setting() {
    let app = App::new("test")
        .global_setting(AppSettings::ColoredHelp)
        .subcommand(SubCommand::with_name("subcmd"));
    assert!(app.p
               .subcommands
               .iter()
               .filter(|s| s.p
                            .meta
                            .name == "subcmd")
               .next()
               .unwrap()
               .p
               .is_set(AppSettings::ColoredHelp));
}

#[test]
fn global_settings() {
    let app = App::new("test")
        .global_settings(&[AppSettings::ColoredHelp, AppSettings::TrailingVarArg])
        .subcommand(SubCommand::with_name("subcmd"));
    assert!(app.p
               .subcommands
               .iter()
               .filter(|s| s.p
                            .meta
                            .name == "subcmd")
               .next()
               .unwrap()
               .p
               .is_set(AppSettings::ColoredHelp));
    assert!(app.p
               .subcommands
               .iter()
               .filter(|s| s.p
                            .meta
                            .name == "subcmd")
               .next()
               .unwrap()
               .p
               .is_set(AppSettings::TrailingVarArg));

}
<|endoftext|>


<commit_before>use std::cmp::{Ord, Ordering, PartialOrd};
use std::fmt;

use ctxt::SemContext;
use driver::cmd::{Args, CollectorName};
use gc::copy::CopyCollector;
use gc::space::{Space, SpaceConfig};
use gc::swiper::Swiper;
use gc::tlab::TLAB_OBJECT_SIZE;
use gc::zero::ZeroCollector;
use mem;
use object::Obj;

pub mod arena;
pub mod bump;
pub mod copy;
pub mod root;
pub mod space;
pub mod swiper;
pub mod tlab;
pub mod zero;

const LARGE_OBJECT_SIZE: usize = 64 * 1024;

const CHUNK_SIZE: usize = 8 * 1024;
pub const DEFAULT_CODE_SPACE_LIMIT: usize = 128 * 1024;
pub const DEFAULT_PERM_SPACE_LIMIT: usize = 64 * 1024;

// every space (eden/from/to/old) is aligned to at least this size
const SPACE_ALIGNMENT_BITS: usize = 16;
const GEN_ALIGNMENT_BITS: usize = 18;

pub struct Gc {
    collector: Box<Collector>,

    code_space: Space,
    perm_space: Space,
}

impl Gc {
    pub fn new(args: &Args) -> Gc {
        let code_config = SpaceConfig {
            executable: true,
            chunk: CHUNK_SIZE,
            limit: args.code_size(),
            align: 64,
        };

        let perm_config = SpaceConfig {
            executable: false,
            chunk: CHUNK_SIZE,
            limit: args.perm_size(),
            align: 8,
        };

        let collector_name = args.flag_gc.unwrap_or(CollectorName::Swiper);

        let collector: Box<Collector> = match collector_name {
            CollectorName::Zero => box ZeroCollector::new(args),
            CollectorName::Copy => box CopyCollector::new(args),
            CollectorName::Swiper => box Swiper::new(args),
        };

        Gc {
            collector: collector,

            code_space: Space::new(code_config, "code"),
            perm_space: Space::new(perm_config, "perm"),
        }
    }

    pub fn needs_write_barrier(&self) -> bool {
        self.collector.needs_write_barrier()
    }

    pub fn card_table_offset(&self) -> usize {
        self.collector.card_table_offset()
    }

    pub fn alloc_code(&self, size: usize) -> *mut u8 {
        self.code_space.alloc(size).to_mut_ptr()
    }

    pub fn alloc_perm(&self, size: usize) -> *mut u8 {
        self.perm_space.alloc(size).to_mut_ptr()
    }

    pub fn alloc(&self, ctxt: &SemContext, size: usize, array_ref: bool) -> Address {
        if ctxt.args.flag_gc_stress_minor {
            self.minor_collect(ctxt);
        }

        if ctxt.args.flag_gc_stress {
            self.collect(ctxt);
        }

        if size < TLAB_OBJECT_SIZE && !ctxt.args.flag_disable_tlab {
            self.alloc_tlab(ctxt, size, array_ref)
        } else if size < LARGE_OBJECT_SIZE {
            self.collector.alloc_normal(ctxt, size, array_ref)
        } else {
            self.collector.alloc_large(ctxt, size, array_ref)
        }
    }

    fn alloc_tlab(&self, ctxt: &SemContext, size: usize, array_ref: bool) -> Address {
        // try to allocate in current tlab
        if let Some(addr) = tlab::allocate(ctxt, size) {
            return addr;
        }

        // if there is not enough space, make heap iterable by filling tlab with unused objects
        tlab::make_iterable(ctxt);

        // allocate new tlab
        if let Some(tlab) = self
            .collector
            .alloc_tlab_area(ctxt, tlab::calculate_size(size))
        {
            let object_start = tlab.start;
            let tlab = Region::new(tlab.start.offset(size), tlab.end);

            // initialize TLAB to new boundaries
            tlab::initialize(ctxt, tlab);

            // object is allocated before TLAB
            object_start
        } else {
            // allocate object
            self.collector.alloc_normal(ctxt, size, array_ref)
        }
    }

    pub fn collect(&self, ctxt: &SemContext) {
        self.collector.collect(ctxt);
    }

    pub fn minor_collect(&self, ctxt: &SemContext) {
        self.collector.minor_collect(ctxt);
    }
}

trait Collector {
    // allocate object of given size
    fn alloc_tlab_area(&self, ctxt: &SemContext, size: usize) -> Option<Region>;
    fn alloc_normal(&self, ctxt: &SemContext, size: usize, array_ref: bool) -> Address;
    fn alloc_large(&self, ctxt: &SemContext, size: usize, array_ref: bool) -> Address;

    // collect garbage
    fn collect(&self, ctxt: &SemContext);

    // collect young generation if supported, otherwise
    // collects whole heap
    fn minor_collect(&self, ctxt: &SemContext);

    // decides whether to emit write barriers needed for
    // generational GC to write into card table
    fn needs_write_barrier(&self) -> bool {
        false
    }

    // only need if write barriers needed
    fn card_table_offset(&self) -> usize {
        0
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct Address(usize);

impl Address {
    #[inline(always)]
    pub fn from(val: usize) -> Address {
        Address(val)
    }

    #[inline(always)]
    pub fn region_start(self, size: usize) -> Region {
        Region::new(self, self.offset(size))
    }

    #[inline(always)]
    pub fn offset_from(self, base: Address) -> usize {
        debug_assert!(self >= base);

        self.to_usize() - base.to_usize()
    }

    #[inline(always)]
    pub fn offset(self, offset: usize) -> Address {
        Address(self.0 + offset)
    }

    #[inline(always)]
    pub fn add_ptr(self, ptr: usize) -> Address {
        Address(self.0 + ptr * mem::ptr_width_usize())
    }

    #[inline(always)]
    pub fn to_mut_obj(self) -> &'static mut Obj {
        unsafe { &mut *self.to_mut_ptr::<Obj>() }
    }

    #[inline(always)]
    pub fn to_obj(self) -> &'static Obj {
        unsafe { &*self.to_mut_ptr::<Obj>() }
    }

    #[inline(always)]
    pub fn to_usize(self) -> usize {
        self.0
    }

    #[inline(always)]
    pub fn from_ptr<T>(ptr: *const T) -> Address {
        Address(ptr as usize)
    }

    #[inline(always)]
    pub fn to_ptr<T>(&self) -> *const T {
        self.0 as *const T
    }

    #[inline(always)]
    pub fn to_mut_ptr<T>(&self) -> *mut T {
        self.0 as *const T as *mut T
    }

    #[inline(always)]
    pub fn null() -> Address {
        Address(0)
    }

    #[inline(always)]
    pub fn is_null(self) -> bool {
        self.0 == 0
    }

    #[inline(always)]
    pub fn is_non_null(self) -> bool {
        self.0 != 0
    }
}

impl fmt::Display for Address {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "0x{:x}", self.to_usize())
    }
}

impl PartialOrd for Address {
    fn partial_cmp(&self, other: &Address) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Address {
    fn cmp(&self, other: &Address) -> Ordering {
        self.to_usize().cmp(&other.to_usize())
    }
}

impl From<usize> for Address {
    fn from(val: usize) -> Address {
        Address(val)
    }
}

#[derive(Clone)]
pub struct Region {
    pub start: Address,
    pub end: Address,
}

impl Region {
    pub fn new(start: Address, end: Address) -> Region {
        Region {
            start: start,
            end: end,
        }
    }

    #[inline(always)]
    pub fn contains(&self, addr: Address) -> bool {
        self.start <= addr && addr < self.end
    }

    #[inline(always)]
    pub fn valid_top(&self, addr: Address) -> bool {
        self.start <= addr && addr <= self.end
    }

    #[inline(always)]
    pub fn size(&self) -> usize {
        self.end.to_usize() - self.start.to_usize()
    }
}

impl fmt::Display for Region {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}-{}", self.start, self.end)
    }
}

struct FormattedSize {
    size: usize,
}

impl fmt::Display for FormattedSize {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let ksize = (self.size as f64) / 1024f64;

        if ksize < 1f64 {
            return write!(f, "{}B", self.size);
        }

        let msize = ksize / 1024f64;

        if msize < 1f64 {
            return write!(f, "{:.1}K", ksize);
        }

        let gsize = msize / 1024f64;

        if gsize < 1f64 {
            write!(f, "{:.1}M", msize)
        } else {
            write!(f, "{:.1}G", gsize)
        }
    }
}

fn formatted_size(size: usize) -> FormattedSize {
    FormattedSize { size }
}

/// round the given value up to the nearest multiple of a space
pub fn align_space(val: usize) -> usize {
    let align = SPACE_ALIGNMENT_BITS;
    // we know that page size is power of 2, hence
    // we can use shifts instead of expensive division
    ((val + (1 << align) - 1) >> align) << align
}

/// round the given value up to the nearest multiple of a generation
pub fn align_gen(val: usize) -> usize {
    let align = GEN_ALIGNMENT_BITS;
    // we know that page size is power of 2, hence
    // we can use shifts instead of expensive division
    ((val + (1 << align) - 1) >> align) << align
}
<commit_msg>gc: Add GcReason enumeration<commit_after>use std::cmp::{Ord, Ordering, PartialOrd};
use std::fmt;

use ctxt::SemContext;
use driver::cmd::{Args, CollectorName};
use gc::copy::CopyCollector;
use gc::space::{Space, SpaceConfig};
use gc::swiper::Swiper;
use gc::tlab::TLAB_OBJECT_SIZE;
use gc::zero::ZeroCollector;
use mem;
use object::Obj;

pub mod arena;
pub mod bump;
pub mod copy;
pub mod root;
pub mod space;
pub mod swiper;
pub mod tlab;
pub mod zero;

const LARGE_OBJECT_SIZE: usize = 64 * 1024;

const CHUNK_SIZE: usize = 8 * 1024;
pub const DEFAULT_CODE_SPACE_LIMIT: usize = 128 * 1024;
pub const DEFAULT_PERM_SPACE_LIMIT: usize = 64 * 1024;

// every space (eden/from/to/old) is aligned to at least this size
const SPACE_ALIGNMENT_BITS: usize = 16;
const GEN_ALIGNMENT_BITS: usize = 18;

pub struct Gc {
    collector: Box<Collector>,

    code_space: Space,
    perm_space: Space,
}

impl Gc {
    pub fn new(args: &Args) -> Gc {
        let code_config = SpaceConfig {
            executable: true,
            chunk: CHUNK_SIZE,
            limit: args.code_size(),
            align: 64,
        };

        let perm_config = SpaceConfig {
            executable: false,
            chunk: CHUNK_SIZE,
            limit: args.perm_size(),
            align: 8,
        };

        let collector_name = args.flag_gc.unwrap_or(CollectorName::Swiper);

        let collector: Box<Collector> = match collector_name {
            CollectorName::Zero => box ZeroCollector::new(args),
            CollectorName::Copy => box CopyCollector::new(args),
            CollectorName::Swiper => box Swiper::new(args),
        };

        Gc {
            collector: collector,

            code_space: Space::new(code_config, "code"),
            perm_space: Space::new(perm_config, "perm"),
        }
    }

    pub fn needs_write_barrier(&self) -> bool {
        self.collector.needs_write_barrier()
    }

    pub fn card_table_offset(&self) -> usize {
        self.collector.card_table_offset()
    }

    pub fn alloc_code(&self, size: usize) -> *mut u8 {
        self.code_space.alloc(size).to_mut_ptr()
    }

    pub fn alloc_perm(&self, size: usize) -> *mut u8 {
        self.perm_space.alloc(size).to_mut_ptr()
    }

    pub fn alloc(&self, ctxt: &SemContext, size: usize, array_ref: bool) -> Address {
        if ctxt.args.flag_gc_stress_minor {
            self.minor_collect(ctxt);
        }

        if ctxt.args.flag_gc_stress {
            self.collect(ctxt);
        }

        if size < TLAB_OBJECT_SIZE && !ctxt.args.flag_disable_tlab {
            self.alloc_tlab(ctxt, size, array_ref)
        } else if size < LARGE_OBJECT_SIZE {
            self.collector.alloc_normal(ctxt, size, array_ref)
        } else {
            self.collector.alloc_large(ctxt, size, array_ref)
        }
    }

    fn alloc_tlab(&self, ctxt: &SemContext, size: usize, array_ref: bool) -> Address {
        // try to allocate in current tlab
        if let Some(addr) = tlab::allocate(ctxt, size) {
            return addr;
        }

        // if there is not enough space, make heap iterable by filling tlab with unused objects
        tlab::make_iterable(ctxt);

        // allocate new tlab
        if let Some(tlab) = self
            .collector
            .alloc_tlab_area(ctxt, tlab::calculate_size(size))
        {
            let object_start = tlab.start;
            let tlab = Region::new(tlab.start.offset(size), tlab.end);

            // initialize TLAB to new boundaries
            tlab::initialize(ctxt, tlab);

            // object is allocated before TLAB
            object_start
        } else {
            // allocate object
            self.collector.alloc_normal(ctxt, size, array_ref)
        }
    }

    pub fn collect(&self, ctxt: &SemContext) {
        self.collector.collect(ctxt);
    }

    pub fn minor_collect(&self, ctxt: &SemContext) {
        self.collector.minor_collect(ctxt);
    }
}

trait Collector {
    // allocate object of given size
    fn alloc_tlab_area(&self, ctxt: &SemContext, size: usize) -> Option<Region>;
    fn alloc_normal(&self, ctxt: &SemContext, size: usize, array_ref: bool) -> Address;
    fn alloc_large(&self, ctxt: &SemContext, size: usize, array_ref: bool) -> Address;

    // collect garbage
    fn collect(&self, ctxt: &SemContext);

    // collect young generation if supported, otherwise
    // collects whole heap
    fn minor_collect(&self, ctxt: &SemContext);

    // decides whether to emit write barriers needed for
    // generational GC to write into card table
    fn needs_write_barrier(&self) -> bool {
        false
    }

    // only need if write barriers needed
    fn card_table_offset(&self) -> usize {
        0
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct Address(usize);

impl Address {
    #[inline(always)]
    pub fn from(val: usize) -> Address {
        Address(val)
    }

    #[inline(always)]
    pub fn region_start(self, size: usize) -> Region {
        Region::new(self, self.offset(size))
    }

    #[inline(always)]
    pub fn offset_from(self, base: Address) -> usize {
        debug_assert!(self >= base);

        self.to_usize() - base.to_usize()
    }

    #[inline(always)]
    pub fn offset(self, offset: usize) -> Address {
        Address(self.0 + offset)
    }

    #[inline(always)]
    pub fn add_ptr(self, ptr: usize) -> Address {
        Address(self.0 + ptr * mem::ptr_width_usize())
    }

    #[inline(always)]
    pub fn to_mut_obj(self) -> &'static mut Obj {
        unsafe { &mut *self.to_mut_ptr::<Obj>() }
    }

    #[inline(always)]
    pub fn to_obj(self) -> &'static Obj {
        unsafe { &*self.to_mut_ptr::<Obj>() }
    }

    #[inline(always)]
    pub fn to_usize(self) -> usize {
        self.0
    }

    #[inline(always)]
    pub fn from_ptr<T>(ptr: *const T) -> Address {
        Address(ptr as usize)
    }

    #[inline(always)]
    pub fn to_ptr<T>(&self) -> *const T {
        self.0 as *const T
    }

    #[inline(always)]
    pub fn to_mut_ptr<T>(&self) -> *mut T {
        self.0 as *const T as *mut T
    }

    #[inline(always)]
    pub fn null() -> Address {
        Address(0)
    }

    #[inline(always)]
    pub fn is_null(self) -> bool {
        self.0 == 0
    }

    #[inline(always)]
    pub fn is_non_null(self) -> bool {
        self.0 != 0
    }
}

impl fmt::Display for Address {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "0x{:x}", self.to_usize())
    }
}

impl PartialOrd for Address {
    fn partial_cmp(&self, other: &Address) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Address {
    fn cmp(&self, other: &Address) -> Ordering {
        self.to_usize().cmp(&other.to_usize())
    }
}

impl From<usize> for Address {
    fn from(val: usize) -> Address {
        Address(val)
    }
}

#[derive(Clone)]
pub struct Region {
    pub start: Address,
    pub end: Address,
}

impl Region {
    pub fn new(start: Address, end: Address) -> Region {
        Region {
            start: start,
            end: end,
        }
    }

    #[inline(always)]
    pub fn contains(&self, addr: Address) -> bool {
        self.start <= addr && addr < self.end
    }

    #[inline(always)]
    pub fn valid_top(&self, addr: Address) -> bool {
        self.start <= addr && addr <= self.end
    }

    #[inline(always)]
    pub fn size(&self) -> usize {
        self.end.to_usize() - self.start.to_usize()
    }
}

impl fmt::Display for Region {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}-{}", self.start, self.end)
    }
}

struct FormattedSize {
    size: usize,
}

impl fmt::Display for FormattedSize {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let ksize = (self.size as f64) / 1024f64;

        if ksize < 1f64 {
            return write!(f, "{}B", self.size);
        }

        let msize = ksize / 1024f64;

        if msize < 1f64 {
            return write!(f, "{:.1}K", ksize);
        }

        let gsize = msize / 1024f64;

        if gsize < 1f64 {
            write!(f, "{:.1}M", msize)
        } else {
            write!(f, "{:.1}G", gsize)
        }
    }
}

fn formatted_size(size: usize) -> FormattedSize {
    FormattedSize { size }
}

/// round the given value up to the nearest multiple of a space
pub fn align_space(val: usize) -> usize {
    let align = SPACE_ALIGNMENT_BITS;
    // we know that page size is power of 2, hence
    // we can use shifts instead of expensive division
    ((val + (1 << align) - 1) >> align) << align
}

/// round the given value up to the nearest multiple of a generation
pub fn align_gen(val: usize) -> usize {
    let align = GEN_ALIGNMENT_BITS;
    // we know that page size is power of 2, hence
    // we can use shifts instead of expensive division
    ((val + (1 << align) - 1) >> align) << align
}

enum GcReason {
    PromotionFailure, ForceCollect, ForceMinorCollect
}<|endoftext|>