<commit_before>mod block;
mod camera;
mod cube;
mod error;
mod event;
mod shader;
mod text;
mod texture;
mod wire_cube;

use std::f32::consts::PI;

use image;
use cgmath::{ Point, Point3, Matrix4 };
use glium::{ self, glutin, DisplayBuild, Surface };
use glium::program::Program;
use glium::glutin::Event as GlEvent;
use glium::backend::glutin_backend::WinRef;

use self::camera::Camera;
use self::text::Text;
use self::error::RenderCreationError;
use self::event::Event;
use game::GameState;

const MOUSE_SENSIVITY: f32 = 0.1;

#[derive(Clone, Copy, Debug)]
pub struct Position {
    pub cube_pos: (i32, i32, i32),
}
implement_vertex!(Position, cube_pos);

#[derive(Clone, Copy, Debug)]
pub enum HDirection {
    Forth,
    Back,
    Left,
    Right,
}

#[derive(Clone, Copy, Debug)]
pub enum VDirection {
    Up,
    Down,
}

pub struct Renderer {
    display: glium::Display,
    cube_program: Program,
    wire_program: Program,
    camera: Camera,
    fov: f32, //in radians
    text: Text,
    stats: bool,
    fill: bool,
    game: GameState,
}

impl Renderer {
    pub fn new() -> Result<Renderer, RenderCreationError<glutin::CreationError>> {
        let display = try!(glutin::WindowBuilder::new()
            .with_depth_buffer(24)
            .build_glium());
        let cube_prog = try!(Program::from_source(
            &display,
            shader::CUBE_VERTEX,
            shader::CUBE_FRAGMENT,
            None,
        ));
        let wire_prog = try!(Program::from_source(
            &display,
            shader::WIRE_VERTEX,
            shader::WIRE_FRAGMENT,
            None,
        ));
        let text = try!(Text::new(&display, "/usr/share/fonts/TTF/NotoSans-Regular.ttf", 24));
        {
            let window = display.get_window().unwrap();
            window.set_cursor(glium::glutin::MouseCursor::Crosshair);
            Renderer::fix_mouse(window);
        }
        Ok(Renderer {
            display: display,
            cube_program: cube_prog,
            wire_program: wire_prog,
            camera: Camera::at(Point3::new(5.0, 5.0, 5.0), Point::origin()),
            fov: PI / 3.0,
            text: text,
            stats: false,
            fill: true,
            game: GameState::new(),
        })
    }

    pub fn game_loop(mut self) {

        let image = image::load(::std::io::Cursor::new(&include_bytes!(
            "../../assets/textures/dirt.png"
        )[..]),image::PNG).unwrap().to_rgba();
        let image_dimensions = image.dimensions();
        let image = glium::texture::RawImage2d::from_raw_rgba_reversed(image.into_raw(), image_dimensions);
        let texture = glium::texture::SrgbTexture2d::new(&self.display, image).unwrap();
        let texture_sampler = glium::uniforms::Sampler::new(&texture)
            .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest)
            .minify_filter(glium::uniforms::MinifySamplerFilter::Nearest);

        let vb_cube = glium::VertexBuffer::new(&self.display, &cube::VERTICES).unwrap();
        let ib_cube = glium::IndexBuffer::new(&self.display, glium::index::PrimitiveType::TrianglesList, &cube::INDICES).unwrap();

        let vb_wire = glium::VertexBuffer::new(&self.display, &wire_cube::VERTICES).unwrap();
        let ib_wire = glium::IndexBuffer::new(&self.display, glium::index::PrimitiveType::LinesList, &wire_cube::INDICES).unwrap();

        loop {
            let mut target = self.display.draw();
            target.clear_color_and_depth((0.0, 0.0, 1.0, 1.0), 1.0);
            //target.clear_depth(1.0);

            let perspective: [[f32; 4]; 4] = self.get_perspective(&target).into();
            let view: [[f32; 4]; 4] = self.camera.view_matrix().into();
            {
                let params = self.get_params();

                let cubes: Vec<_> = self.game.stones.into_iter().map(|x| Position { cube_pos: (0, 0, x as i32) }).collect();
                let cubes_buffer = glium::vertex::VertexBuffer::new(&self.display, &cubes).unwrap();

                let wires: Vec<Position> = self.game.stones.into_iter().min().map(|x| Position { cube_pos: (0, 0, x as i32) }).into_iter().collect();
                let wires_buffer = glium::vertex::VertexBuffer::new(&self.display, &wires).unwrap();

                target.draw(
                    (&vb_cube, cubes_buffer.per_instance().unwrap()),
                    &ib_cube,
                    &self.cube_program,
                    &uniform! {
                        model: block::MODEL,
                        view: view,
                        perspective: perspective,
                        tex: texture_sampler,
                    },
                    &params
                ).unwrap();
                target.draw(
                    (&vb_wire, wires_buffer.per_instance().unwrap()),
                    &ib_wire,
                    &self.wire_program,
                    &uniform! { model: block::MODEL, view: view, perspective: perspective },
                    &params
                ).unwrap();

                if self.stats {
                    self.text.draw(&mut target, &format!("{}", self.camera), (1.0, 1.0, 0.0, 1.0));
                }
            }
            target.finish().unwrap();

            {
                let window = self.display.get_window().unwrap();
                window.set_cursor(glium::glutin::MouseCursor::Crosshair);
            }

            if !self.handle_events() {
                return;
            }
            self.camera.update();
        }
    }

    fn get_perspective<T: Surface>(&self, surface: &T) -> Matrix4<f32> {
        let (width, height) = surface.get_dimensions();
        let aspect_ratio = height as f32 / width as f32;

        let zfar = 1024.0;
        let znear = 0.1;

        let f = 1.0 / (self.fov / 2.0).tan();

        Matrix4::new(
            f * aspect_ratio, 0.0,              0.0              , 0.0,
                   0.0      ,  f ,              0.0              , 0.0,
                   0.0      , 0.0,      (zfar+znear)/(zfar-znear), 1.0,
                   0.0      , 0.0, -(2.0*zfar*znear)/(zfar-znear), 0.0,
        )
    }

    fn get_params(&self) -> glium::DrawParameters {
        use glium::draw_parameters::PolygonMode::{ Fill, Line };
        use glium::draw_parameters::DepthTest;
        glium::DrawParameters {
            depth: glium::Depth {
                test: DepthTest::IfLess,
                write: true,
                .. Default::default()
            },
            //backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise,
            polygon_mode: if self.fill { Fill } else { Line },
            .. Default::default()
        }
    }

    fn convert(ev: GlEvent) -> Option<Event> {
        use self::event::Event::*;
        use self::HDirection::*;
        use self::VDirection::*;
        use glium::glutin::Event as E;
        use glium::glutin::ElementState::Pressed;
        use glium::glutin::VirtualKeyCode as V;
        match ev {
            E::KeyboardInput(state, _, Some(key)) => {
                let t = state == Pressed;
                match (state, key) {
                    (Pressed, V::Key1)   => Some(ToogleBlock { block: 1 }),
                    (Pressed, V::Key2)   => Some(ToogleBlock { block: 2 }),
                    (Pressed, V::Key3)   => Some(ToogleBlock { block: 3 }),
                    (Pressed, V::Key4)   => Some(ToogleBlock { block: 4 }),
                    (Pressed, V::Key5)   => Some(ToogleBlock { block: 5 }),
                    (Pressed, V::Key6)   => Some(ToogleBlock { block: 6 }),
                    (Pressed, V::Key7)   => Some(ToogleBlock { block: 7 }),
                    (Pressed, V::Key8)   => Some(ToogleBlock { block: 8 }),
                    (Pressed, V::Key9)   => Some(ToogleBlock { block: 9 }),
                    (_      , V::W)      => Some(Move { dir: Forth, toogle: t }),
                    (_      , V::A)      => Some(Move { dir: Left , toogle: t }),
                    (_      , V::S)      => Some(Move { dir: Back , toogle: t }),
                    (_      , V::D)      => Some(Move { dir: Right, toogle: t }),
                    (_      , V::Up)     => Some(Turn { dir: Forth, toogle: t }),
                    (_      , V::Left)   => Some(Turn { dir: Left , toogle: t }),
                    (_      , V::Down)   => Some(Turn { dir: Back , toogle: t }),
                    (_      , V::Right)  => Some(Turn { dir: Right, toogle: t }),
                    (_      , V::Space)  => Some(Fly  { dir: Up   , toogle: t }),
                    (_      , V::LShift) => Some(Fly  { dir: Down , toogle: t }),
                    _ => None,
                }

            },
            _ => None,
        }
    }

    fn handle_events(&mut self) -> bool {
        for ev in self.display.poll_events() {
            use glium::glutin::Event as E;
            use glium::glutin::ElementState::Pressed;
            use glium::glutin::VirtualKeyCode::*;
            match ev {
                E::Closed => return false,
                E::MouseMoved((mouse_x, mouse_y)) => {
                    let window = self.display.get_window().unwrap();
                    let (mid_x, mid_y) = Renderer::fix_mouse(window);
                    // screen coordinates increase to the right, just like phi
                    self.camera.add_phi((mouse_x - mid_x as i32) as f32 * MOUSE_SENSIVITY);
                    // screen coordinates decrease to the top, unlike theta
                    self.camera.add_theta((mid_y - mouse_y as i32) as f32 * MOUSE_SENSIVITY);
                },
                E::KeyboardInput(Pressed, _, Some(Escape)) => return false,
                _ => if let Some(ev) = Renderer::convert(ev) {
                    use self::event::Event::*;
                    match ev {
                        ToogleBlock { block: n }   => self.game.flip_stone(n),
                        Move { dir: d, toogle: t } => self.camera.mov (d, t),
                        Turn { dir: d, toogle: t } => self.camera.turn(d, t),
                        Fly  { dir: d, toogle: t } => self.camera.fly (d, t),
                        _ => {}
                    }
                },
            }
        }
        return true;
    }

    //returns mouse delta
    fn fix_mouse(window: WinRef) -> (i32, i32) {
        let (x, y) = window.get_inner_size_points().unwrap();
        let (mid_x, mid_y) = (x as i32 / 2, y as i32 / 2);
        window.set_cursor_position(mid_x, mid_y).unwrap();
        (mid_x, mid_y)
    }
}
<commit_msg>moving stuff out of loops<commit_after>mod block;
mod camera;
mod cube;
mod error;
mod event;
mod shader;
mod text;
mod texture;
mod wire_cube;

use std::f32::consts::PI;

use image;
use cgmath::{ Point, Point3, Matrix4 };
use glium::{ self, glutin, DisplayBuild, Surface };
use glium::program::Program;
use glium::glutin::Event as GlEvent;
use glium::backend::glutin_backend::WinRef;

use self::camera::Camera;
use self::text::Text;
use self::error::RenderCreationError;
use self::event::Event;
use game::GameState;

const MOUSE_SENSIVITY: f32 = 0.1;

#[derive(Clone, Copy, Debug)]
pub struct Position {
    pub cube_pos: (i32, i32, i32),
}
implement_vertex!(Position, cube_pos);

#[derive(Clone, Copy, Debug)]
pub enum HDirection {
    Forth,
    Back,
    Left,
    Right,
}

#[derive(Clone, Copy, Debug)]
pub enum VDirection {
    Up,
    Down,
}

pub struct Renderer {
    display: glium::Display,
    cube_program: Program,
    wire_program: Program,
    camera: Camera,
    fov: f32, //in radians
    text: Text,
    stats: bool,
    fill: bool,
    game: GameState,
}

impl Renderer {
    pub fn new() -> Result<Renderer, RenderCreationError<glutin::CreationError>> {
        let display = try!(glutin::WindowBuilder::new()
            .with_depth_buffer(24)
            .build_glium());
        let cube_prog = try!(Program::from_source(
            &display,
            shader::CUBE_VERTEX,
            shader::CUBE_FRAGMENT,
            None,
        ));
        let wire_prog = try!(Program::from_source(
            &display,
            shader::WIRE_VERTEX,
            shader::WIRE_FRAGMENT,
            None,
        ));
        let text = try!(Text::new(&display, "/usr/share/fonts/TTF/NotoSans-Regular.ttf", 24));
        {
            let window = display.get_window().unwrap();
            window.set_cursor(glium::glutin::MouseCursor::Crosshair);
            Renderer::fix_mouse(window);
        }
        Ok(Renderer {
            display: display,
            cube_program: cube_prog,
            wire_program: wire_prog,
            camera: Camera::at(Point3::new(5.0, 5.0, 5.0), Point::origin()),
            fov: PI / 3.0,
            text: text,
            stats: false,
            fill: true,
            game: GameState::new(),
        })
    }

    pub fn game_loop(mut self) {

        let image = image::load(::std::io::Cursor::new(&include_bytes!(
            "../../assets/textures/dirt.png"
        )[..]),image::PNG).unwrap().to_rgba();
        let image_dimensions = image.dimensions();
        let image = glium::texture::RawImage2d::from_raw_rgba_reversed(image.into_raw(), image_dimensions);
        let texture = glium::texture::SrgbTexture2d::new(&self.display, image).unwrap();
        let texture_sampler = glium::uniforms::Sampler::new(&texture)
            .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest)
            .minify_filter(glium::uniforms::MinifySamplerFilter::Nearest);

        let vb_cube = glium::VertexBuffer::new(&self.display, &cube::VERTICES).unwrap();
        let ib_cube = glium::IndexBuffer::new(&self.display, glium::index::PrimitiveType::TrianglesList, &cube::INDICES).unwrap();

        let vb_wire = glium::VertexBuffer::new(&self.display, &wire_cube::VERTICES).unwrap();
        let ib_wire = glium::IndexBuffer::new(&self.display, glium::index::PrimitiveType::LinesList, &wire_cube::INDICES).unwrap();

        loop {
            let mut target = self.display.draw();
            target.clear_color_and_depth((0.0, 0.0, 1.0, 1.0), 1.0);
            //target.clear_depth(1.0);

            let perspective: [[f32; 4]; 4] = self.get_perspective(&target).into();
            let view: [[f32; 4]; 4] = self.camera.view_matrix().into();
            {
                let params = self.get_params();

                let cubes: Vec<_> = self.game.stones.into_iter().map(|x| Position { cube_pos: (0, 0, x as i32) }).collect();
                let cubes_buffer = glium::vertex::VertexBuffer::new(&self.display, &cubes).unwrap();

                let wires: Vec<Position> = self.game.stones.into_iter().min().map(|x| Position { cube_pos: (0, 0, x as i32) }).into_iter().collect();
                let wires_buffer = glium::vertex::VertexBuffer::new(&self.display, &wires).unwrap();

                target.draw(
                    (&vb_cube, cubes_buffer.per_instance().unwrap()),
                    &ib_cube,
                    &self.cube_program,
                    &uniform! {
                        model: block::MODEL,
                        view: view,
                        perspective: perspective,
                        tex: texture_sampler,
                    },
                    &params
                ).unwrap();
                target.draw(
                    (&vb_wire, wires_buffer.per_instance().unwrap()),
                    &ib_wire,
                    &self.wire_program,
                    &uniform! { model: block::MODEL, view: view, perspective: perspective },
                    &params
                ).unwrap();

                if self.stats {
                    self.text.draw(&mut target, &format!("{}", self.camera), (1.0, 1.0, 0.0, 1.0));
                }
            }
            target.finish().unwrap();

            if !self.handle_events() {
                return;
            }
            self.camera.update();
        }
    }

    fn get_perspective<T: Surface>(&self, surface: &T) -> Matrix4<f32> {
        let (width, height) = surface.get_dimensions();
        let aspect_ratio = height as f32 / width as f32;

        let zfar = 1024.0;
        let znear = 0.1;

        let f = 1.0 / (self.fov / 2.0).tan();

        Matrix4::new(
            f * aspect_ratio, 0.0,              0.0              , 0.0,
                   0.0      ,  f ,              0.0              , 0.0,
                   0.0      , 0.0,      (zfar+znear)/(zfar-znear), 1.0,
                   0.0      , 0.0, -(2.0*zfar*znear)/(zfar-znear), 0.0,
        )
    }

    fn get_params(&self) -> glium::DrawParameters {
        use glium::draw_parameters::PolygonMode::{ Fill, Line };
        use glium::draw_parameters::DepthTest;
        glium::DrawParameters {
            depth: glium::Depth {
                test: DepthTest::IfLess,
                write: true,
                .. Default::default()
            },
            //backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise,
            polygon_mode: if self.fill { Fill } else { Line },
            .. Default::default()
        }
    }

    fn convert(ev: GlEvent) -> Option<Event> {
        use self::event::Event::*;
        use self::HDirection::*;
        use self::VDirection::*;
        use glium::glutin::Event as E;
        use glium::glutin::ElementState::Pressed;
        use glium::glutin::VirtualKeyCode as V;
        match ev {
            E::KeyboardInput(state, _, Some(key)) => {
                let t = state == Pressed;
                match (state, key) {
                    (Pressed, V::Key1)   => Some(ToogleBlock { block: 1 }),
                    (Pressed, V::Key2)   => Some(ToogleBlock { block: 2 }),
                    (Pressed, V::Key3)   => Some(ToogleBlock { block: 3 }),
                    (Pressed, V::Key4)   => Some(ToogleBlock { block: 4 }),
                    (Pressed, V::Key5)   => Some(ToogleBlock { block: 5 }),
                    (Pressed, V::Key6)   => Some(ToogleBlock { block: 6 }),
                    (Pressed, V::Key7)   => Some(ToogleBlock { block: 7 }),
                    (Pressed, V::Key8)   => Some(ToogleBlock { block: 8 }),
                    (Pressed, V::Key9)   => Some(ToogleBlock { block: 9 }),
                    (_      , V::W)      => Some(Move { dir: Forth, toogle: t }),
                    (_      , V::A)      => Some(Move { dir: Left , toogle: t }),
                    (_      , V::S)      => Some(Move { dir: Back , toogle: t }),
                    (_      , V::D)      => Some(Move { dir: Right, toogle: t }),
                    (_      , V::Up)     => Some(Turn { dir: Forth, toogle: t }),
                    (_      , V::Left)   => Some(Turn { dir: Left , toogle: t }),
                    (_      , V::Down)   => Some(Turn { dir: Back , toogle: t }),
                    (_      , V::Right)  => Some(Turn { dir: Right, toogle: t }),
                    (_      , V::Space)  => Some(Fly  { dir: Up   , toogle: t }),
                    (_      , V::LShift) => Some(Fly  { dir: Down , toogle: t }),
                    _ => None,
                }

            },
            _ => None,
        }
    }

    fn handle_events(&mut self) -> bool {
        for ev in self.display.poll_events() {
            use glium::glutin::Event as E;
            use glium::glutin::ElementState::Pressed;
            use glium::glutin::VirtualKeyCode::*;
            match ev {
                E::Closed => return false,
                E::MouseMoved((mouse_x, mouse_y)) => {
                    let window = self.display.get_window().unwrap();
                    let (mid_x, mid_y) = Renderer::fix_mouse(window);
                    // screen coordinates increase to the right, just like phi
                    self.camera.add_phi((mouse_x - mid_x as i32) as f32 * MOUSE_SENSIVITY);
                    // screen coordinates decrease to the top, unlike theta
                    self.camera.add_theta((mid_y - mouse_y as i32) as f32 * MOUSE_SENSIVITY);
                },
                E::KeyboardInput(Pressed, _, Some(Escape)) => return false,
                _ => if let Some(ev) = Renderer::convert(ev) {
                    use self::event::Event::*;
                    match ev {
                        ToogleBlock { block: n }   => self.game.flip_stone(n),
                        Move { dir: d, toogle: t } => self.camera.mov (d, t),
                        Turn { dir: d, toogle: t } => self.camera.turn(d, t),
                        Fly  { dir: d, toogle: t } => self.camera.fly (d, t),
                        _ => {}
                    }
                },
            }
        }
        return true;
    }

    //returns mouse delta
    fn fix_mouse(window: WinRef) -> (i32, i32) {
        let (x, y) = window.get_inner_size_points().unwrap();
        let (mid_x, mid_y) = (x as i32 / 2, y as i32 / 2);
        window.set_cursor_position(mid_x, mid_y).unwrap();
        (mid_x, mid_y)
    }
}
<|endoftext|>


<commit_before>use std::io::Read;

use crate::common::LineIter;

fn read_input(input: &mut dyn Read) -> (i32, i32) {
    let mut reader = LineIter::new(input);

    let a = reader
        .next()
        .unwrap()
        .split(' ')
        .last()
        .unwrap()
        .parse()
        .unwrap();

    let b = reader
        .next()
        .unwrap()
        .split(' ')
        .last()
        .unwrap()
        .parse()
        .unwrap();

    (a, b)
}

#[inline]
fn simulate(die: i32, pos: i32) -> i32 {
    (pos + 3 * die + 3 - 1) % 10 + 1
}

fn find_repetition(mut pos: i32, mut die: i32) -> i32 {
    let mut advance = 0;

    for _ in 0..10 {
        pos = simulate(die, pos);
        advance += pos;
        die = (die + 6) % 10;
    }

    advance
}

pub fn part1(input: &mut dyn Read) -> String {
    const TARGET_SCORE: i32 = 1000;

    let (a, b) = read_input(input);

    let a10 = find_repetition(a, 1);
    let b10 = find_repetition(b, 4);

    let a_win = TARGET_SCORE / a10;
    let b_win = TARGET_SCORE / b10;

    let mut rolls = 3 * 20 * a_win.min(b_win);
    let mut a_score = rolls / 3 / 20 * a10;
    let mut b_score = rolls / 3 / 20 * b10;
    let mut a_pos = a;
    let mut b_pos = b;
    let mut die = 1;

    let (loser_score, rolls) = if a_win < b_win {
        while a_score < TARGET_SCORE {
            a_pos = simulate(die, a_pos);
            a_score += a_pos;
            rolls += 3;

            if a_score < TARGET_SCORE {
                b_pos = simulate(die + 3, b_pos);
                b_score += b_pos;
                rolls += 3;
            }

            die = (die + 6) % 10;
        }

        (b_score, rolls)
    } else {
        while b_score < TARGET_SCORE {
            a_pos = simulate(die, a_pos);
            a_score += a_pos;

            b_pos = simulate(die + 3, b_pos);
            b_score += b_pos;

            rolls += 6;

            die = (die + 6) % 10;
        }

        (a_score, rolls)
    };

    (loser_score * rolls).to_string()
}

const MAX_TURNS: usize = 21; // Trivial upper bound, could be lower
const ROLLS: [i32; 7] = [3, 4, 5, 6, 7, 8, 9];
const FREQS: [u64; 7] = [1, 3, 6, 7, 6, 3, 1];

fn multiverse_recursive(
    remaining: &mut [u64],
    wins: &mut [u64],
    turn: usize,
    alive: u64,
    pos: i32,
    score: i32,
) {
    for (roll, freq) in ROLLS.into_iter().zip(FREQS) {
        let new_pos = (pos + roll - 1) % 10 + 1;
        let new_score = score + new_pos;
        let new_alive = alive * freq;

        if new_score >= 21 {
            wins[turn] += new_alive;
        } else {
            remaining[turn] += new_alive;
            multiverse_recursive(remaining, wins, turn + 1, new_alive, new_pos, new_score);
        }
    }
}

fn multiverse(pos: i32) -> ([u64; MAX_TURNS], [u64; MAX_TURNS]) {
    let mut alive = [0; MAX_TURNS];
    let mut wins = [0; MAX_TURNS];

    alive[0] = 1;

    multiverse_recursive(&mut alive, &mut wins, 1, 1, pos, 0);

    (wins, alive)
}

pub fn part2(input: &mut dyn Read) -> String {
    let (a, b) = read_input(input);

    let (a_wins, a_alive) = multiverse(a);
    let (b_wins, b_alive) = multiverse(b);

    let a_winner: u64 = a_wins[1..].iter().zip(b_alive).map(|(&a, b)| a * b).sum();
    let b_winner: u64 = b_wins.into_iter().zip(a_alive).map(|(a, b)| a * b).sum();

    a_winner.max(b_winner).to_string()
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE: &[u8] = include_bytes!("samples/21.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 739785);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 444356092776315u64);
    }
}
<commit_msg>Replace inefficient recursion with iteration<commit_after>use std::io::Read;

use crate::common::LineIter;

fn read_input(input: &mut dyn Read) -> (i32, i32) {
    let mut reader = LineIter::new(input);

    let a = reader
        .next()
        .unwrap()
        .split(' ')
        .last()
        .unwrap()
        .parse()
        .unwrap();

    let b = reader
        .next()
        .unwrap()
        .split(' ')
        .last()
        .unwrap()
        .parse()
        .unwrap();

    (a, b)
}

#[inline]
fn simulate(die: i32, pos: i32) -> i32 {
    (pos + 3 * die + 3 - 1) % 10 + 1
}

fn find_repetition(mut pos: i32, mut die: i32) -> i32 {
    let mut advance = 0;

    for _ in 0..10 {
        pos = simulate(die, pos);
        advance += pos;
        die = (die + 6) % 10;
    }

    advance
}

pub fn part1(input: &mut dyn Read) -> String {
    const TARGET_SCORE: i32 = 1000;

    let (a, b) = read_input(input);

    let a10 = find_repetition(a, 1);
    let b10 = find_repetition(b, 4);

    let a_win = TARGET_SCORE / a10;
    let b_win = TARGET_SCORE / b10;

    let mut rolls = 3 * 20 * a_win.min(b_win);
    let mut a_score = rolls / 3 / 20 * a10;
    let mut b_score = rolls / 3 / 20 * b10;
    let mut a_pos = a;
    let mut b_pos = b;
    let mut die = 1;

    let (loser_score, rolls) = if a_win < b_win {
        while a_score < TARGET_SCORE {
            a_pos = simulate(die, a_pos);
            a_score += a_pos;
            rolls += 3;

            if a_score < TARGET_SCORE {
                b_pos = simulate(die + 3, b_pos);
                b_score += b_pos;
                rolls += 3;
            }

            die = (die + 6) % 10;
        }

        (b_score, rolls)
    } else {
        while b_score < TARGET_SCORE {
            a_pos = simulate(die, a_pos);
            a_score += a_pos;

            b_pos = simulate(die + 3, b_pos);
            b_score += b_pos;

            rolls += 6;

            die = (die + 6) % 10;
        }

        (a_score, rolls)
    };

    (loser_score * rolls).to_string()
}

const MAX_TURNS: usize = 13;
const ROLLS: [i32; 7] = [3, 4, 5, 6, 7, 8, 9];
const FREQS: [u64; 7] = [1, 3, 6, 7, 6, 3, 1];

fn multiverse(pos: i32) -> ([u64; MAX_TURNS], [u64; MAX_TURNS]) {
    type State = [[u64; 10]; 21];

    // Let's work with pos - 1 as pos for now, indexes nicer;
    let pos = pos as usize - 1;

    let mut alive = [0; MAX_TURNS];
    let mut wins = [0; MAX_TURNS];

    alive[0] = 1;

    let mut current = [[0u64; 10]; 21];
    current[0][pos] = 1;
    let mut next = [[0u64; 10]; 21];

    let mut helper = |turn, current: &State, next: &mut State| {
        next.iter_mut().flatten().for_each(|v| *v = 0);

        for (score, score_row) in current.iter().enumerate() {
            for (pos, &pop) in score_row.iter().enumerate() {
                for (roll, freq) in ROLLS.into_iter().zip(FREQS) {
                    let new_pos = (pos + (roll as usize)) % 10;
                    let new_score = score + new_pos + 1; // +1 because above
                    let new_pop = freq * pop;

                    if new_score >= next.len() {
                        wins[turn] += new_pop;
                    } else {
                        alive[turn] += new_pop;
                        next[new_score][new_pos] += new_pop;
                    }
                }
            }
        }
    };

    for turn in (1..MAX_TURNS).step_by(2) {
        helper(turn, &current, &mut next);
        helper(turn + 1, &next, &mut current);
    }

    (wins, alive)
}

pub fn part2(input: &mut dyn Read) -> String {
    let (a, b) = read_input(input);

    let (a_wins, a_alive) = multiverse(a);
    let (b_wins, b_alive) = multiverse(b);

    let a_winner: u64 = a_wins[1..].iter().zip(b_alive).map(|(&a, b)| a * b).sum();
    let b_winner: u64 = b_wins.into_iter().zip(a_alive).map(|(a, b)| a * b).sum();

    a_winner.max(b_winner).to_string()
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE: &[u8] = include_bytes!("samples/21.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 739785);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 444356092776315u64);
    }
}
<|endoftext|>


<commit_before>/*
 * diosix microkernel 'menchi'
 *
 * Allocate and deallocate physical memory in x86 systems
 *
 * Maintainer: Chris Williams (diosix.org)
 *
 */

use core::mem::size_of;
use core::ptr;
use spin::Mutex;
use errors::KernelInternalError;

use ::hardware::physmem;

const PAGE_STACK_PHYS_START: usize = 4 * 1024 * 1024; /* start page stack at 4MB mark in physical memory */

/* create a system-wide physical stack with a locking mechanism. */
pub static SYSTEMSTACK: Mutex<PageStack> = Mutex::new(PageStack
                                           {
                                                base: PAGE_STACK_PHYS_START,
                                                ptr: 0,
                                                max_ptr: 0,
                                                size: 0,
                                                virtual_translation_offset: 0,
                                           });

/* page stack design notes
 *
 * Each 1GB of physical RAM takes up 2MB of RAM: 262,144 x 8-byte pointers.
 * Each stacked pointer is the base address of a 4K physical page frame.
 * Pop the stack to obtain a pointer to an available 4K physical page.
 * Push the stack to return a 4K physical page for reuse.
 *
 * Start stack at the 4MB mark in physical RAM. Consider a sub-stack per CPU core?
 *
 * We force Rust to give stack_base a fixed kernel address, pointing to the
 * base of the stack (natch). We then manipulate the memory aboveit using
 * .offset(). stack_ptr is the offset to the first empty slot, so 0 means an
 * empty stack and stack_max means a full stack. */

/* stack_base          stack_base + stack_ptr             stack_base + stack_max
 * |                   |                                  |
 * ++++++++++++++++++++...................................|
 *
 * Where + means a page is available and . means zero (no page)
 * stack_max is calculated from the total number of available physical pages.
 */

pub struct PageStack
{
    base: usize,     /* base physical address of this stack */
    max_ptr: usize,  /* ptr cannot be greater than stack_max */
    ptr: usize,      /* index into the stack, start form zero at the base */
    size: usize,     /* total size of the stack in memory in bytes */

    /* when we need to convert a physical RAM address to a kernel virtual
     * address, allowing the kernel to use the address as a pointer,
     * add virtual_translation_offset to the physical address.
     * at boot this is zero, but eventually all the kernel's physical
     * memory accesses occur in the upper kernel memory area. */
    virtual_translation_offset: usize,
}

impl PageStack
{
    /* phys_to_kernel_virt
     *
     * Convert a physical RAM address to a kernel virtual address.
     * The kernel eventually maps all of physical memory into an
     * upper kernel-only space. All physical memory accesses must
     * happen in this high virtual area. This function converts
     * physical addresses into kernel-accessible virtual addresses.
     * => phys = physical address to translate
     * <= returns kernel virtual address
     */
    fn phys_to_kernel_virt(&self, phys: usize) -> usize
    {
        phys + self.virtual_translation_offset
    }

    /* set_kernel_translation_offset
     *
     * Set the offset used to translate physical RAM addresses into
     * kernel-accessible virtual addresses. See phys_to_kernel_virt()
     * for more info.
     * => offset = value added to physical addresses to translate
     *             them into kernel virtual adddresses.
     */
    pub fn set_kernel_translation_offset(&mut self, offset: usize)
    {
        self.virtual_translation_offset = offset;
    }

    /* set_limit
     *
     * Set the maximum number of entries allowed in the stack. You cannot
     * shrink the stack limit below the stack pointer. That would be unwise.
     * => limit = max number of pointer entries in the stack
     * <= returns error code on failure.
     */
    pub fn set_limit(&mut self, limit: usize) -> Result<(), KernelInternalError>
    {
        if limit < self.ptr
        {
            return Err(KernelInternalError::BadPgStackLimit);
        }

        self.max_ptr = limit;
        self.size = limit * size_of::<usize>();
        Ok(())
    }

    /* return true if addr falls within the physical address range of the
     * page stack, or false if not. needed to ensure we don't reuse physical
     * memory already in use by the page stack. */
    pub fn check_collision(&mut self, addr: usize) -> bool
    {
        if addr >= self.base && addr < (self.base + self.size) { return true; }

        false
    }

    /* push
     *
     * Stash a 4K page's physical address on top of the stack and increment
     * the stack pointer. The address must be 4K aligned and the stack
     * must not be full.
     * => phys_addr = physical address to push onto the stack
     * <= returns error code on failure.
     */
    pub fn push(&mut self, phys_addr: usize) -> Result<(), KernelInternalError>
    {
        /* make sure we're not about to overflow the stack.
         * If ptr > max then something's gone really wrong.
         * move this into a sanity check elsewhere in the kernel? */
        if self.ptr >= self.max_ptr
        {
            return Err(KernelInternalError::PgStackFull);
        }

        /* make sure the physical address given is sane - it must
         * be aligned to the nearest 4K, thus the lowest 12 bits
         * must be clear. */
        if phys_addr & (physmem::SMALL_PAGE_SIZE - 1) != 0
        {
            return Err(KernelInternalError::BadPhysPgAddress);
        }

        /* calculate the virtual address we need to write to in the stack,
         * write to it, and increment the stack pointer */
        unsafe
        {
            ptr::write(self.ptr_to_kernel_addr() as *mut _, phys_addr);
        }

        self.ptr = self.ptr + 1;
        Ok(())
    }

    /* pop
     *
     * Pop a 4K page's base physical address off the stack, if available.
     * <= returns address or error code on failure.
     */
    pub fn pop(&mut self) -> Result<usize, KernelInternalError>
    {
        /* bail out if there are no physical pages available (gulp) */
        if self.ptr == 0
        {
            return Err(KernelInternalError::NoPhysPgAvailable)
        }

        /* decrement the ptr, get the physical page address from the stack,
         * and then zero the stack entry. use virtual addresses to access
         * the stack. */
        self.ptr = self.ptr - 1;
        let ptr_addr = self.ptr_to_kernel_addr();

        let pg_addr = unsafe
        {
            ptr::read(ptr_addr as *mut _)
        };

        unsafe
        {
            ptr::write(ptr_addr as *mut _, 0);
        }

        kprintln!("--> popped {:x} off page stack", pg_addr);
        Ok(pg_addr)
    }

    /* ptr_to_kernel_addr
     *
     * Calculate the kernel virtual address of the stack entry for the current ptr
     * so it can be used to access that particular stack entry.
     * <= returns kernel virtual address for the stack ptr entry
     */
    fn ptr_to_kernel_addr(&mut self) -> usize
    {
        self.phys_to_kernel_virt(self.base + (self.ptr * size_of::<usize>()))
    }
}

<commit_msg>[x86] remove debug from pgstacks.rs<commit_after>/*
 * diosix microkernel 'menchi'
 *
 * Allocate and deallocate physical memory in x86 systems
 *
 * Maintainer: Chris Williams (diosix.org)
 *
 */

use core::mem::size_of;
use core::ptr;
use spin::Mutex;
use errors::KernelInternalError;

use ::hardware::physmem;

const PAGE_STACK_PHYS_START: usize = 4 * 1024 * 1024; /* start page stack at 4MB mark in physical memory */

/* create a system-wide physical stack with a locking mechanism. */
pub static SYSTEMSTACK: Mutex<PageStack> = Mutex::new(PageStack
                                           {
                                                base: PAGE_STACK_PHYS_START,
                                                ptr: 0,
                                                max_ptr: 0,
                                                size: 0,
                                                virtual_translation_offset: 0,
                                           });

/* page stack design notes
 *
 * Each 1GB of physical RAM takes up 2MB of RAM: 262,144 x 8-byte pointers.
 * Each stacked pointer is the base address of a 4K physical page frame.
 * Pop the stack to obtain a pointer to an available 4K physical page.
 * Push the stack to return a 4K physical page for reuse.
 *
 * Start stack at the 4MB mark in physical RAM. Consider a sub-stack per CPU core?
 *
 * We force Rust to give stack_base a fixed kernel address, pointing to the
 * base of the stack (natch). We then manipulate the memory aboveit using
 * .offset(). stack_ptr is the offset to the first empty slot, so 0 means an
 * empty stack and stack_max means a full stack. */

/* stack_base          stack_base + stack_ptr             stack_base + stack_max
 * |                   |                                  |
 * ++++++++++++++++++++...................................|
 *
 * Where + means a page is available and . means zero (no page)
 * stack_max is calculated from the total number of available physical pages.
 */

pub struct PageStack
{
    base: usize,     /* base physical address of this stack */
    max_ptr: usize,  /* ptr cannot be greater than stack_max */
    ptr: usize,      /* index into the stack, start form zero at the base */
    size: usize,     /* total size of the stack in memory in bytes */

    /* when we need to convert a physical RAM address to a kernel virtual
     * address, allowing the kernel to use the address as a pointer,
     * add virtual_translation_offset to the physical address.
     * at boot this is zero, but eventually all the kernel's physical
     * memory accesses occur in the upper kernel memory area. */
    virtual_translation_offset: usize,
}

impl PageStack
{
    /* phys_to_kernel_virt
     *
     * Convert a physical RAM address to a kernel virtual address.
     * The kernel eventually maps all of physical memory into an
     * upper kernel-only space. All physical memory accesses must
     * happen in this high virtual area. This function converts
     * physical addresses into kernel-accessible virtual addresses.
     * => phys = physical address to translate
     * <= returns kernel virtual address
     */
    fn phys_to_kernel_virt(&self, phys: usize) -> usize
    {
        phys + self.virtual_translation_offset
    }

    /* set_kernel_translation_offset
     *
     * Set the offset used to translate physical RAM addresses into
     * kernel-accessible virtual addresses. See phys_to_kernel_virt()
     * for more info.
     * => offset = value added to physical addresses to translate
     *             them into kernel virtual adddresses.
     */
    pub fn set_kernel_translation_offset(&mut self, offset: usize)
    {
        self.virtual_translation_offset = offset;
    }

    /* set_limit
     *
     * Set the maximum number of entries allowed in the stack. You cannot
     * shrink the stack limit below the stack pointer. That would be unwise.
     * => limit = max number of pointer entries in the stack
     * <= returns error code on failure.
     */
    pub fn set_limit(&mut self, limit: usize) -> Result<(), KernelInternalError>
    {
        if limit < self.ptr
        {
            return Err(KernelInternalError::BadPgStackLimit);
        }

        self.max_ptr = limit;
        self.size = limit * size_of::<usize>();
        Ok(())
    }

    /* return true if addr falls within the physical address range of the
     * page stack, or false if not. needed to ensure we don't reuse physical
     * memory already in use by the page stack. */
    pub fn check_collision(&mut self, addr: usize) -> bool
    {
        if addr >= self.base && addr < (self.base + self.size) { return true; }

        false
    }

    /* push
     *
     * Stash a 4K page's physical address on top of the stack and increment
     * the stack pointer. The address must be 4K aligned and the stack
     * must not be full.
     * => phys_addr = physical address to push onto the stack
     * <= returns error code on failure.
     */
    pub fn push(&mut self, phys_addr: usize) -> Result<(), KernelInternalError>
    {
        /* make sure we're not about to overflow the stack.
         * If ptr > max then something's gone really wrong.
         * move this into a sanity check elsewhere in the kernel? */
        if self.ptr >= self.max_ptr
        {
            return Err(KernelInternalError::PgStackFull);
        }

        /* make sure the physical address given is sane - it must
         * be aligned to the nearest 4K, thus the lowest 12 bits
         * must be clear. */
        if phys_addr & (physmem::SMALL_PAGE_SIZE - 1) != 0
        {
            return Err(KernelInternalError::BadPhysPgAddress);
        }

        /* calculate the virtual address we need to write to in the stack,
         * write to it, and increment the stack pointer */
        unsafe
        {
            ptr::write(self.ptr_to_kernel_addr() as *mut _, phys_addr);
        }

        self.ptr = self.ptr + 1;
        Ok(())
    }

    /* pop
     *
     * Pop a 4K page's base physical address off the stack, if available.
     * <= returns address or error code on failure.
     */
    pub fn pop(&mut self) -> Result<usize, KernelInternalError>
    {
        /* bail out if there are no physical pages available (gulp) */
        if self.ptr == 0
        {
            return Err(KernelInternalError::NoPhysPgAvailable)
        }

        /* decrement the ptr, get the physical page address from the stack,
         * and then zero the stack entry. use virtual addresses to access
         * the stack. */
        self.ptr = self.ptr - 1;
        let ptr_addr = self.ptr_to_kernel_addr();

        let pg_addr = unsafe
        {
            ptr::read(ptr_addr as *mut _)
        };

        unsafe
        {
            ptr::write(ptr_addr as *mut _, 0);
        }

        Ok(pg_addr)
    }

    /* ptr_to_kernel_addr
     *
     * Calculate the kernel virtual address of the stack entry for the current ptr
     * so it can be used to access that particular stack entry.
     * <= returns kernel virtual address for the stack ptr entry
     */
    fn ptr_to_kernel_addr(&mut self) -> usize
    {
        self.phys_to_kernel_virt(self.base + (self.ptr * size_of::<usize>()))
    }
}

<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate clap;
extern crate shadowsocks;
extern crate qrcode;

use std::str;
use std::net::SocketAddr;

use rustc_serialize::base64::{FromBase64, ToBase64, URL_SAFE};
use rustc_serialize::json::{ToJson, as_pretty_json};

use clap::{App, Arg};

use qrcode::QrCode;

use shadowsocks::VERSION;
use shadowsocks::config::{Config, ConfigType, ServerConfig};

const BLACK: &'static str = "\x1b[40m  \x1b[0m";
const WHITE: &'static str = "\x1b[47m  \x1b[0m";

fn encode_url(svr: &ServerConfig) -> String {
    let url = format!("{}:{}@{}",
                      svr.method().to_string(),
                      svr.password(),
                      svr.addr());
    format!("ss://{}", url.as_bytes().to_base64(URL_SAFE))
}

fn print_qrcode(encoded: &str) {
    let qrcode = QrCode::new(encoded.as_bytes()).unwrap();

    for _ in 0..qrcode.width() + 2 {
        print!("{}", WHITE);
    }
    println!("");

    for y in 0..qrcode.width() {
        print!("{}", WHITE);
        for x in 0..qrcode.width() {
            let color = if qrcode[(x, y)] { BLACK } else { WHITE };

            print!("{}", color);
        }
        println!("{}", WHITE);
    }

    for _ in 0..qrcode.width() + 2 {
        print!("{}", WHITE);
    }
    println!("");
}

fn encode(filename: &str, need_qrcode: bool) {
    let config = Config::load_from_file(filename, ConfigType::Server).unwrap();

    for svr in config.server {
        let encoded = encode_url(&svr);

        println!("{}", encoded);

        if need_qrcode {
            print_qrcode(&encoded);
        }
    }
}

fn decode(encoded: &str, need_qrcode: bool) {
    if !encoded.starts_with("ss://") {
        panic!("Malformed input: {:?}", encoded);
    }

    let decoded = encoded[5..].from_base64().unwrap();
    let decoded = str::from_utf8(&decoded).unwrap();

    let mut sp1 = decoded.split('@');
    let (account, addr) = match (sp1.next(), sp1.next()) {
        (Some(account), Some(addr)) => (account, addr),
        _ => panic!("Malformed input"),
    };

    let mut sp2 = account.split(':');
    let (method, pwd) = match (sp2.next(), sp2.next()) {
        (Some(m), Some(p)) => (m, p),
        _ => panic!("Malformed input"),
    };

    let addr = match addr.parse::<SocketAddr>() {
        Ok(a) => a,
        Err(err) => panic!("Malformed input: {:?}", err),
    };

    let svrconfig = ServerConfig::basic(addr, pwd.to_owned(), method.parse().unwrap());

    let mut config = Config::new();
    config.server.push(svrconfig);

    let config_json = config.to_json();
    println!("{}", as_pretty_json(&config_json));

    if need_qrcode {
        print_qrcode(encoded);
    }
}

fn main() {
    let app = App::new("ssurl")
        .author("Y. T. Chung <zonyitoo@gmail.com>")
        .about("Encode and decode ShadowSocks URL")
        .version(VERSION)
        .arg(Arg::with_name("ENCODE")
            .short("e")
            .long("encode")
            .takes_value(true)
            .help("Encode the server configuration in the provided JSON file"))
        .arg(Arg::with_name("DECODE")
            .short("d")
            .long("decode")
            .takes_value(true)
            .help("Decode the server configuration from the provide ShadowSocks URL"))
        .arg(Arg::with_name("QRCODE")
            .short("c")
            .long("qrcode")
            .takes_value(false)
            .help("Generate the QRCode with the provided configuration"));
    let matches = app.get_matches();

    let need_qrcode = matches.is_present("QRCODE");

    if let Some(file) = matches.value_of("ENCODE") {
        encode(file, need_qrcode);
    } else if let Some(encoded) = matches.value_of("DECODE") {
        decode(encoded, need_qrcode);
    } else {
        println!("Use -h for more detail");
    }
}
<commit_msg>ssurl support ServerAddr<commit_after>extern crate rustc_serialize;
extern crate clap;
extern crate shadowsocks;
extern crate qrcode;

use std::str;

use rustc_serialize::base64::{FromBase64, ToBase64, URL_SAFE};
use rustc_serialize::json::{ToJson, as_pretty_json};

use clap::{App, Arg};

use qrcode::QrCode;

use shadowsocks::VERSION;
use shadowsocks::config::{Config, ConfigType, ServerConfig, ServerAddr};

const BLACK: &'static str = "\x1b[40m  \x1b[0m";
const WHITE: &'static str = "\x1b[47m  \x1b[0m";

fn encode_url(svr: &ServerConfig) -> String {
    let url = format!("{}:{}@{}",
                      svr.method().to_string(),
                      svr.password(),
                      svr.addr());
    format!("ss://{}", url.as_bytes().to_base64(URL_SAFE))
}

fn print_qrcode(encoded: &str) {
    let qrcode = QrCode::new(encoded.as_bytes()).unwrap();

    for _ in 0..qrcode.width() + 2 {
        print!("{}", WHITE);
    }
    println!("");

    for y in 0..qrcode.width() {
        print!("{}", WHITE);
        for x in 0..qrcode.width() {
            let color = if qrcode[(x, y)] { BLACK } else { WHITE };

            print!("{}", color);
        }
        println!("{}", WHITE);
    }

    for _ in 0..qrcode.width() + 2 {
        print!("{}", WHITE);
    }
    println!("");
}

fn encode(filename: &str, need_qrcode: bool) {
    let config = Config::load_from_file(filename, ConfigType::Server).unwrap();

    for svr in config.server {
        let encoded = encode_url(&svr);

        println!("{}", encoded);

        if need_qrcode {
            print_qrcode(&encoded);
        }
    }
}

fn decode(encoded: &str, need_qrcode: bool) {
    if !encoded.starts_with("ss://") {
        panic!("Malformed input: {:?}", encoded);
    }

    let decoded = encoded[5..].from_base64().unwrap();
    let decoded = str::from_utf8(&decoded).unwrap();

    let mut sp1 = decoded.split('@');
    let (account, addr) = match (sp1.next(), sp1.next()) {
        (Some(account), Some(addr)) => (account, addr),
        _ => panic!("Malformed input"),
    };

    let mut sp2 = account.split(':');
    let (method, pwd) = match (sp2.next(), sp2.next()) {
        (Some(m), Some(p)) => (m, p),
        _ => panic!("Malformed input"),
    };

    let addr = match addr.parse::<ServerAddr>() {
        Ok(a) => a,
        Err(err) => panic!("Malformed input: {:?}", err),
    };

    let svrconfig = ServerConfig::new(addr, pwd.to_owned(), method.parse().unwrap(), None);

    let mut config = Config::new();
    config.server.push(svrconfig);

    let config_json = config.to_json();
    println!("{}", as_pretty_json(&config_json));

    if need_qrcode {
        print_qrcode(encoded);
    }
}

fn main() {
    let app = App::new("ssurl")
        .author("Y. T. Chung <zonyitoo@gmail.com>")
        .about("Encode and decode ShadowSocks URL")
        .version(VERSION)
        .arg(Arg::with_name("ENCODE")
            .short("e")
            .long("encode")
            .takes_value(true)
            .help("Encode the server configuration in the provided JSON file"))
        .arg(Arg::with_name("DECODE")
            .short("d")
            .long("decode")
            .takes_value(true)
            .help("Decode the server configuration from the provide ShadowSocks URL"))
        .arg(Arg::with_name("QRCODE")
            .short("c")
            .long("qrcode")
            .takes_value(false)
            .help("Generate the QRCode with the provided configuration"));
    let matches = app.get_matches();

    let need_qrcode = matches.is_present("QRCODE");

    if let Some(file) = matches.value_of("ENCODE") {
        encode(file, need_qrcode);
    } else if let Some(encoded) = matches.value_of("DECODE") {
        decode(encoded, need_qrcode);
    } else {
        println!("Use -h for more detail");
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate arg_parser;
extern crate coreutils;
extern crate extra;

use std::env;
use std::path::Path;
use std::process::exit;
use std::io::{stdout, stderr, Write};
use arg_parser::ArgParser;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = r#"
NAME
    which - locate a command
SYNOPSIS
    which [ -h | --help ]
DESCRIPTION
    which prints the full path of shell commands
OPTIONS
    -h
    --help
        Print this manual page.
"#; /* @MANEND */

fn main() {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();
    let mut parser = ArgParser::new(1).add_flag(&["h", "help"]);
    parser.parse(env::args());

    if parser.found("help") {
        stdout.write(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        exit(0);
    }

    if parser.args.is_empty() {
        stderr.write(b"Please provide a program name\n").try(&mut stderr);
        stderr.flush().try(&mut stderr);
        exit(1);
    }

    let mut string = String::new();
    let path = env::var("PATH").unwrap();

    let read_dir = Path::new(&path).read_dir().try(&mut stderr);
    let binaries: Vec<String> = read_dir
        .filter_map(|x| x.ok())
        .map(|dir| {
            let mut file_name = dir.file_name().to_string_lossy().into_owned();
            if dir.file_type().try(&mut stderr).is_dir() {
                file_name.push('/');
            }
            file_name
        })
        .collect();

    for program in parser.args.iter() {
        if binaries.contains(program) {
            string.push_str(&path);
            string.push('/');
            string.push_str(program);
        } else {
            string.push_str(program);
            string.push_str(" not found");
        }
        string.push('\n');
    }

    stdout.write(string.as_bytes()).try(&mut stderr);
}
<commit_msg>Make 'which' work with multiple directories in PATH<commit_after>#![deny(warnings)]

extern crate arg_parser;
extern crate coreutils;
extern crate extra;

use std::env;
use std::process::exit;
use std::io::{stdout, stderr, Write};
use arg_parser::ArgParser;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = r#"
NAME
    which - locate a command
SYNOPSIS
    which [ -h | --help ]
DESCRIPTION
    which prints the full path of shell commands
OPTIONS
    -h
    --help
        Print this manual page.
"#; /* @MANEND */

fn main() {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();
    let mut parser = ArgParser::new(1).add_flag(&["h", "help"]);
    parser.parse(env::args());

    if parser.found("help") {
        stdout.write(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        exit(0);
    }

    if parser.args.is_empty() {
        stderr.write(b"Please provide a program name\n").try(&mut stderr);
        stderr.flush().try(&mut stderr);
        exit(1);
    }

    let paths = env::var("PATH").unwrap();

    for program in parser.args.iter() {
        let mut executable_path = None;

        for mut path in env::split_paths(&paths) {
            path.push(program);
            if path.exists() {
                executable_path = Some(path);
                break;
            }
        }

        if let Some(path) = executable_path {
            println!("{}", path.display());
        } else {
            println!("{} not found", program);
        }
    }
}
<|endoftext|>


<commit_before>#![deny(unused)]
#![cfg_attr(test, deny(warnings))]
#![recursion_limit="128"]

// Currently, Cargo does not use clippy for its source code.
// But if someone runs it they should know that
// @alexcrichton disagree with clippy on some style things
#![cfg_attr(feature = "cargo-clippy", allow(explicit_iter_loop))]

#[macro_use] extern crate failure;
#[macro_use] extern crate log;
#[macro_use] extern crate scoped_tls;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate serde_json;
extern crate atty;
extern crate crates_io as registry;
extern crate crossbeam;
extern crate curl;
extern crate docopt;
extern crate filetime;
extern crate flate2;
extern crate fs2;
extern crate git2;
extern crate glob;
extern crate hex;
extern crate home;
extern crate ignore;
extern crate jobserver;
extern crate lazycell;
extern crate libc;
extern crate libgit2_sys;
extern crate num_cpus;
extern crate same_file;
extern crate semver;
extern crate serde;
extern crate serde_ignored;
extern crate shell_escape;
extern crate tar;
extern crate tempdir;
extern crate termcolor;
extern crate toml;
extern crate url;
#[cfg(target_os = "macos")]
extern crate core_foundation;

use std::fmt;

use serde::de::DeserializeOwned;
use serde::ser;
use docopt::Docopt;
use failure::Error;

use core::Shell;
use core::shell::Verbosity::Verbose;

pub use util::{CargoError, CargoResult, CliError, CliResult, Config};
pub use util::errors::Internal;

pub const CARGO_ENV: &str = "CARGO";

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub struct CommitInfo {
    pub short_commit_hash: String,
    pub commit_hash: String,
    pub commit_date: String,
}

pub struct CfgInfo {
    // Information about the git repository we may have been built from.
    pub commit_info: Option<CommitInfo>,
    // The release channel we were built for.
    pub release_channel: String,
}

pub struct VersionInfo {
    pub major: u8,
    pub minor: u8,
    pub patch: u8,
    pub pre_release: Option<String>,
    // Information that's only available when we were built with
    // configure/make, rather than cargo itself.
    pub cfg_info: Option<CfgInfo>,
}

impl fmt::Display for VersionInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "cargo {}.{}.{}",
               self.major, self.minor, self.patch)?;
        if let Some(channel) = self.cfg_info.as_ref().map(|ci| &ci.release_channel) {
            if channel != "stable" {
                write!(f, "-{}", channel)?;
                let empty = String::from("");
                write!(f, "{}", self.pre_release.as_ref().unwrap_or(&empty))?;
            }
        };

        if let Some(ref cfg) = self.cfg_info {
            if let Some(ref ci) = cfg.commit_info {
                write!(f, " ({} {})",
                       ci.short_commit_hash, ci.commit_date)?;
            }
        };
        Ok(())
    }
}

pub fn call_main_without_stdin<Flags: DeserializeOwned>(
            exec: fn(Flags, &mut Config) -> CliResult,
            config: &mut Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult
{
    let docopt = Docopt::new(usage).unwrap()
        .options_first(options_first)
        .argv(args.iter().map(|s| &s[..]))
        .help(true);

    let flags = docopt.deserialize().map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::new(e.into(), code)
    })?;

    exec(flags, config)
}

pub fn print_json<T: ser::Serialize>(obj: &T) {
    let encoded = serde_json::to_string(&obj).unwrap();
    println!("{}", encoded);
}

pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
    debug!("exit_with_error; err={:?}", err);

    let CliError { error, exit_code, unknown } = err;
    // exit_code == 0 is non-fatal error, e.g. docopt version info
    let fatal = exit_code != 0;

    let hide = unknown && shell.verbosity() != Verbose;

    if let Some(error) = error {
        if hide {
            drop(shell.error("An unknown error occurred"))
        } else if fatal {
            drop(shell.error(&error))
        } else {
            println!("{}", error);
        }

        if !handle_cause(&error, shell) || hide {
            drop(writeln!(shell.err(), "\nTo learn more, run the command again \
                                        with --verbose."));
        }
    }

    std::process::exit(exit_code)
}

pub fn handle_error(err: CargoError, shell: &mut Shell) {
    debug!("handle_error; err={:?}", &err);

    let _ignored_result = shell.error(&err);
    handle_cause(&err, shell);
}

fn handle_cause(cargo_err: &Error, shell: &mut Shell) -> bool {
    fn print(error: String, shell: &mut Shell) {
        drop(writeln!(shell.err(), "\nCaused by:"));
        drop(writeln!(shell.err(), "  {}", error));
    }

    let verbose = shell.verbosity();

    if verbose == Verbose {
        // The first error has already been printed to the shell
        // Print all remaining errors
        for err in cargo_err.causes().skip(1) {
            print(err.to_string(), shell);
        }
    } else {
        // The first error has already been printed to the shell
        // Print remaining errors until one marked as Internal appears
        for err in cargo_err.causes().skip(1) {
            if err.downcast_ref::<Internal>().is_some() {
                return false;
            }

            print(err.to_string(), shell);
        }
    }

    true
}

pub fn version() -> VersionInfo {
    macro_rules! option_env_str {
        ($name:expr) => { option_env!($name).map(|s| s.to_string()) }
    }

    // So this is pretty horrible...
    // There are two versions at play here:
    //   - version of cargo-the-binary, which you see when you type `cargo --version`
    //   - version of cargo-the-library, which you download from crates.io for use
    //     in your projects.
    //
    // We want to make the `binary` version the same as the corresponding Rust/rustc release.
    // At the same time, we want to keep the library version at `0.x`, because Cargo as
    // a library is (and probably will always be) unstable.
    //
    // Historically, Cargo used the same version number for both the binary and the library.
    // Specifically, rustc 1.x.z was paired with cargo 0.x+1.w.
    // We continue to use this scheme for the library, but transform it to 1.x.w for the purposes
    // of `cargo --version`.
    let major = 1;
    let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap() - 1;
    let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u8>().unwrap();

    match option_env!("CFG_RELEASE_CHANNEL") {
        // We have environment variables set up from configure/make.
        Some(_) => {
            let commit_info =
                option_env!("CFG_COMMIT_HASH").map(|s| {
                    CommitInfo {
                        commit_hash: s.to_string(),
                        short_commit_hash: option_env_str!("CFG_SHORT_COMMIT_HASH").unwrap(),
                        commit_date: option_env_str!("CFG_COMMIT_DATE").unwrap(),
                    }
                });
            VersionInfo {
                major,
                minor,
                patch,
                pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
                cfg_info: Some(CfgInfo {
                    release_channel: option_env_str!("CFG_RELEASE_CHANNEL").unwrap(),
                    commit_info,
                }),
            }
        },
        // We are being compiled by Cargo itself.
        None => {
            VersionInfo {
                major,
                minor,
                patch,
                pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
                cfg_info: None,
            }
        }
    }
}
<commit_msg>Clippy complains about unused in derived code.<commit_after>#![cfg_attr(test, deny(warnings))]
#![recursion_limit="128"]

// Currently, Cargo does not use clippy for its source code.
// But if someone runs it they should know that
// @alexcrichton disagree with clippy on some style things
#![cfg_attr(feature = "cargo-clippy", allow(explicit_iter_loop))]

#[macro_use] extern crate failure;
#[macro_use] extern crate log;
#[macro_use] extern crate scoped_tls;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate serde_json;
extern crate atty;
extern crate crates_io as registry;
extern crate crossbeam;
extern crate curl;
extern crate docopt;
extern crate filetime;
extern crate flate2;
extern crate fs2;
extern crate git2;
extern crate glob;
extern crate hex;
extern crate home;
extern crate ignore;
extern crate jobserver;
extern crate lazycell;
extern crate libc;
extern crate libgit2_sys;
extern crate num_cpus;
extern crate same_file;
extern crate semver;
extern crate serde;
extern crate serde_ignored;
extern crate shell_escape;
extern crate tar;
extern crate tempdir;
extern crate termcolor;
extern crate toml;
extern crate url;
#[cfg(target_os = "macos")]
extern crate core_foundation;

use std::fmt;

use serde::de::DeserializeOwned;
use serde::ser;
use docopt::Docopt;
use failure::Error;

use core::Shell;
use core::shell::Verbosity::Verbose;

pub use util::{CargoError, CargoResult, CliError, CliResult, Config};
pub use util::errors::Internal;

pub const CARGO_ENV: &str = "CARGO";

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub struct CommitInfo {
    pub short_commit_hash: String,
    pub commit_hash: String,
    pub commit_date: String,
}

pub struct CfgInfo {
    // Information about the git repository we may have been built from.
    pub commit_info: Option<CommitInfo>,
    // The release channel we were built for.
    pub release_channel: String,
}

pub struct VersionInfo {
    pub major: u8,
    pub minor: u8,
    pub patch: u8,
    pub pre_release: Option<String>,
    // Information that's only available when we were built with
    // configure/make, rather than cargo itself.
    pub cfg_info: Option<CfgInfo>,
}

impl fmt::Display for VersionInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "cargo {}.{}.{}",
               self.major, self.minor, self.patch)?;
        if let Some(channel) = self.cfg_info.as_ref().map(|ci| &ci.release_channel) {
            if channel != "stable" {
                write!(f, "-{}", channel)?;
                let empty = String::from("");
                write!(f, "{}", self.pre_release.as_ref().unwrap_or(&empty))?;
            }
        };

        if let Some(ref cfg) = self.cfg_info {
            if let Some(ref ci) = cfg.commit_info {
                write!(f, " ({} {})",
                       ci.short_commit_hash, ci.commit_date)?;
            }
        };
        Ok(())
    }
}

pub fn call_main_without_stdin<Flags: DeserializeOwned>(
            exec: fn(Flags, &mut Config) -> CliResult,
            config: &mut Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult
{
    let docopt = Docopt::new(usage).unwrap()
        .options_first(options_first)
        .argv(args.iter().map(|s| &s[..]))
        .help(true);

    let flags = docopt.deserialize().map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::new(e.into(), code)
    })?;

    exec(flags, config)
}

pub fn print_json<T: ser::Serialize>(obj: &T) {
    let encoded = serde_json::to_string(&obj).unwrap();
    println!("{}", encoded);
}

pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
    debug!("exit_with_error; err={:?}", err);

    let CliError { error, exit_code, unknown } = err;
    // exit_code == 0 is non-fatal error, e.g. docopt version info
    let fatal = exit_code != 0;

    let hide = unknown && shell.verbosity() != Verbose;

    if let Some(error) = error {
        if hide {
            drop(shell.error("An unknown error occurred"))
        } else if fatal {
            drop(shell.error(&error))
        } else {
            println!("{}", error);
        }

        if !handle_cause(&error, shell) || hide {
            drop(writeln!(shell.err(), "\nTo learn more, run the command again \
                                        with --verbose."));
        }
    }

    std::process::exit(exit_code)
}

pub fn handle_error(err: CargoError, shell: &mut Shell) {
    debug!("handle_error; err={:?}", &err);

    let _ignored_result = shell.error(&err);
    handle_cause(&err, shell);
}

fn handle_cause(cargo_err: &Error, shell: &mut Shell) -> bool {
    fn print(error: String, shell: &mut Shell) {
        drop(writeln!(shell.err(), "\nCaused by:"));
        drop(writeln!(shell.err(), "  {}", error));
    }

    let verbose = shell.verbosity();

    if verbose == Verbose {
        // The first error has already been printed to the shell
        // Print all remaining errors
        for err in cargo_err.causes().skip(1) {
            print(err.to_string(), shell);
        }
    } else {
        // The first error has already been printed to the shell
        // Print remaining errors until one marked as Internal appears
        for err in cargo_err.causes().skip(1) {
            if err.downcast_ref::<Internal>().is_some() {
                return false;
            }

            print(err.to_string(), shell);
        }
    }

    true
}

pub fn version() -> VersionInfo {
    macro_rules! option_env_str {
        ($name:expr) => { option_env!($name).map(|s| s.to_string()) }
    }

    // So this is pretty horrible...
    // There are two versions at play here:
    //   - version of cargo-the-binary, which you see when you type `cargo --version`
    //   - version of cargo-the-library, which you download from crates.io for use
    //     in your projects.
    //
    // We want to make the `binary` version the same as the corresponding Rust/rustc release.
    // At the same time, we want to keep the library version at `0.x`, because Cargo as
    // a library is (and probably will always be) unstable.
    //
    // Historically, Cargo used the same version number for both the binary and the library.
    // Specifically, rustc 1.x.z was paired with cargo 0.x+1.w.
    // We continue to use this scheme for the library, but transform it to 1.x.w for the purposes
    // of `cargo --version`.
    let major = 1;
    let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap() - 1;
    let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u8>().unwrap();

    match option_env!("CFG_RELEASE_CHANNEL") {
        // We have environment variables set up from configure/make.
        Some(_) => {
            let commit_info =
                option_env!("CFG_COMMIT_HASH").map(|s| {
                    CommitInfo {
                        commit_hash: s.to_string(),
                        short_commit_hash: option_env_str!("CFG_SHORT_COMMIT_HASH").unwrap(),
                        commit_date: option_env_str!("CFG_COMMIT_DATE").unwrap(),
                    }
                });
            VersionInfo {
                major,
                minor,
                patch,
                pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
                cfg_info: Some(CfgInfo {
                    release_channel: option_env_str!("CFG_RELEASE_CHANNEL").unwrap(),
                    commit_info,
                }),
            }
        },
        // We are being compiled by Cargo itself.
        None => {
            VersionInfo {
                major,
                minor,
                patch,
                pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
                cfg_info: None,
            }
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::AttrBinding;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::global;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers};
use dom::node::Node;
use dom::window::Window;
use dom::virtualmethods::vtable_for;

use devtools_traits::AttrInfo;
use servo_util::str::{DOMString, split_html_space_chars};
use std::cell::{Ref, RefCell};
use std::mem;
use string_cache::{Atom, Namespace};

pub enum AttrSettingType {
    FirstSetAttr,
    ReplacedAttr,
}

#[deriving(PartialEq, Clone)]
#[jstraceable]
pub enum AttrValue {
    StringAttrValue(DOMString),
    TokenListAttrValue(DOMString, Vec<Atom>),
    UIntAttrValue(DOMString, u32),
    AtomAttrValue(Atom),
}

impl AttrValue {
    pub fn from_tokenlist(tokens: DOMString) -> AttrValue {
        let atoms = split_html_space_chars(tokens.as_slice())
            .map(|token| Atom::from_slice(token)).collect();
        TokenListAttrValue(tokens, atoms)
    }

    pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
        let result: u32 = from_str(string.as_slice()).unwrap_or(default);
        UIntAttrValue(string, result)
    }

    pub fn from_atomic(string: DOMString) -> AttrValue {
        let value = Atom::from_slice(string.as_slice());
        AtomAttrValue(value)
    }

    pub fn tokens<'a>(&'a self) -> Option<&'a [Atom]> {
        match *self {
            TokenListAttrValue(_, ref tokens) => Some(tokens.as_slice()),
            _ => None
        }
    }
}

impl Str for AttrValue {
    fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            StringAttrValue(ref value) |
            TokenListAttrValue(ref value, _) |
            UIntAttrValue(ref value, _) => value.as_slice(),
            AtomAttrValue(ref value) => value.as_slice(),
        }
    }
}

#[jstraceable]
#[must_root]
#[privatize]
pub struct Attr {
    reflector_: Reflector,
    local_name: Atom,
    value: RefCell<AttrValue>,
    name: Atom,
    namespace: Namespace,
    prefix: Option<DOMString>,

    /// the element that owns this attribute.
    owner: JS<Element>,
}

impl Reflectable for Attr {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl Attr {
    fn new_inherited(local_name: Atom, value: AttrValue,
                     name: Atom, namespace: Namespace,
                     prefix: Option<DOMString>, owner: JSRef<Element>) -> Attr {
        Attr {
            reflector_: Reflector::new(),
            local_name: local_name,
            value: RefCell::new(value),
            name: name,
            namespace: namespace,
            prefix: prefix,
            owner: JS::from_rooted(owner),
        }
    }

    pub fn new(window: JSRef<Window>, local_name: Atom, value: AttrValue,
               name: Atom, namespace: Namespace,
               prefix: Option<DOMString>, owner: JSRef<Element>) -> Temporary<Attr> {
        reflect_dom_object(box Attr::new_inherited(local_name, value, name, namespace, prefix, owner),
                           &global::Window(window), AttrBinding::Wrap)
    }

    #[inline]
    pub fn name<'a>(&'a self) -> &'a Atom {
        &self.name
    }

    #[inline]
    pub fn namespace<'a>(&'a self) -> &'a Namespace {
        &self.namespace
    }

    #[inline]
    pub fn prefix<'a>(&'a self) -> &'a Option<DOMString> {
        &self.prefix
    }
}

impl<'a> AttrMethods for JSRef<'a, Attr> {
    fn LocalName(self) -> DOMString {
        self.local_name().as_slice().to_string()
    }

    fn Value(self) -> DOMString {
        self.value().as_slice().to_string()
    }

    fn SetValue(self, value: DOMString) {
        let owner = self.owner.root();
        let value = owner.parse_attribute(&self.namespace, self.local_name(), value);
        self.set_value(ReplacedAttr, value);
    }

    fn Name(self) -> DOMString {
        self.name.as_slice().to_string()
    }

    fn GetNamespaceURI(self) -> Option<DOMString> {
        let Namespace(ref atom) = self.namespace;
        match atom.as_slice() {
            "" => None,
            url => Some(url.to_string()),
        }
    }

    fn GetPrefix(self) -> Option<DOMString> {
        self.prefix.clone()
    }
}

pub trait AttrHelpers<'a> {
    fn set_value(self, set_type: AttrSettingType, value: AttrValue);
    fn value(self) -> Ref<'a, AttrValue>;
    fn local_name(self) -> &'a Atom;
    fn summarize(self) -> AttrInfo;
}

impl<'a> AttrHelpers<'a> for JSRef<'a, Attr> {
    fn set_value(self, set_type: AttrSettingType, value: AttrValue) {
        let owner = self.owner.root();
        let node: JSRef<Node> = NodeCast::from_ref(*owner);
        let namespace_is_null = self.namespace == ns!("");

        match set_type {
            ReplacedAttr => {
                if namespace_is_null {
                    vtable_for(&node).before_remove_attr(
                        self.local_name(),
                        self.value().as_slice().to_string())
                }
            }
            FirstSetAttr => {}
        }

        *self.value.borrow_mut() = value;

        if namespace_is_null {
            vtable_for(&node).after_set_attr(
                self.local_name(),
                self.value().as_slice().to_string())
        }
    }

    fn value(self) -> Ref<'a, AttrValue> {
        self.extended_deref().value.borrow()
    }

    fn local_name(self) -> &'a Atom {
        &self.extended_deref().local_name
    }

    fn summarize(self) -> AttrInfo {
        let Namespace(ref ns) = self.namespace;
        AttrInfo {
            namespace: ns.as_slice().to_string(),
            name: self.Name(),
            value: self.Value(),
        }
    }
}

pub trait AttrHelpersForLayout {
    unsafe fn value_ref_forever(&self) -> &'static str;
    unsafe fn value_atom_forever(&self) -> Option<Atom>;
    unsafe fn value_tokens_forever(&self) -> Option<&'static [Atom]>;
    unsafe fn local_name_atom_forever(&self) -> Atom;
}

impl AttrHelpersForLayout for Attr {
    #[inline]
    unsafe fn value_ref_forever(&self) -> &'static str {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(&self.value);
        value.as_slice()
    }

    #[inline]
    unsafe fn value_atom_forever(&self) -> Option<Atom> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(&self.value);
        match *value {
            AtomAttrValue(ref val) => Some(val.clone()),
            _ => None,
        }
    }

    #[inline]
    unsafe fn value_tokens_forever(&self) -> Option<&'static [Atom]> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(&self.value);
        match *value {
            TokenListAttrValue(_, ref tokens) => Some(tokens.as_slice()),
            _ => None,
        }
    }

    #[inline]
    unsafe fn local_name_atom_forever(&self) -> Atom {
        self.local_name.clone()
    }
}
<commit_msg>Use DOMRefCell<T> in Attr.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::{DOMRefCell, Ref};
use dom::bindings::codegen::Bindings::AttrBinding;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::global;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers};
use dom::node::Node;
use dom::window::Window;
use dom::virtualmethods::vtable_for;

use devtools_traits::AttrInfo;
use servo_util::str::{DOMString, split_html_space_chars};
use std::mem;
use string_cache::{Atom, Namespace};

pub enum AttrSettingType {
    FirstSetAttr,
    ReplacedAttr,
}

#[deriving(PartialEq, Clone)]
#[jstraceable]
pub enum AttrValue {
    StringAttrValue(DOMString),
    TokenListAttrValue(DOMString, Vec<Atom>),
    UIntAttrValue(DOMString, u32),
    AtomAttrValue(Atom),
}

impl AttrValue {
    pub fn from_tokenlist(tokens: DOMString) -> AttrValue {
        let atoms = split_html_space_chars(tokens.as_slice())
            .map(|token| Atom::from_slice(token)).collect();
        TokenListAttrValue(tokens, atoms)
    }

    pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
        let result: u32 = from_str(string.as_slice()).unwrap_or(default);
        UIntAttrValue(string, result)
    }

    pub fn from_atomic(string: DOMString) -> AttrValue {
        let value = Atom::from_slice(string.as_slice());
        AtomAttrValue(value)
    }

    pub fn tokens<'a>(&'a self) -> Option<&'a [Atom]> {
        match *self {
            TokenListAttrValue(_, ref tokens) => Some(tokens.as_slice()),
            _ => None
        }
    }
}

impl Str for AttrValue {
    fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            StringAttrValue(ref value) |
            TokenListAttrValue(ref value, _) |
            UIntAttrValue(ref value, _) => value.as_slice(),
            AtomAttrValue(ref value) => value.as_slice(),
        }
    }
}

#[jstraceable]
#[must_root]
#[privatize]
pub struct Attr {
    reflector_: Reflector,
    local_name: Atom,
    value: DOMRefCell<AttrValue>,
    name: Atom,
    namespace: Namespace,
    prefix: Option<DOMString>,

    /// the element that owns this attribute.
    owner: JS<Element>,
}

impl Reflectable for Attr {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl Attr {
    fn new_inherited(local_name: Atom, value: AttrValue,
                     name: Atom, namespace: Namespace,
                     prefix: Option<DOMString>, owner: JSRef<Element>) -> Attr {
        Attr {
            reflector_: Reflector::new(),
            local_name: local_name,
            value: DOMRefCell::new(value),
            name: name,
            namespace: namespace,
            prefix: prefix,
            owner: JS::from_rooted(owner),
        }
    }

    pub fn new(window: JSRef<Window>, local_name: Atom, value: AttrValue,
               name: Atom, namespace: Namespace,
               prefix: Option<DOMString>, owner: JSRef<Element>) -> Temporary<Attr> {
        reflect_dom_object(box Attr::new_inherited(local_name, value, name, namespace, prefix, owner),
                           &global::Window(window), AttrBinding::Wrap)
    }

    #[inline]
    pub fn name<'a>(&'a self) -> &'a Atom {
        &self.name
    }

    #[inline]
    pub fn namespace<'a>(&'a self) -> &'a Namespace {
        &self.namespace
    }

    #[inline]
    pub fn prefix<'a>(&'a self) -> &'a Option<DOMString> {
        &self.prefix
    }
}

impl<'a> AttrMethods for JSRef<'a, Attr> {
    fn LocalName(self) -> DOMString {
        self.local_name().as_slice().to_string()
    }

    fn Value(self) -> DOMString {
        self.value().as_slice().to_string()
    }

    fn SetValue(self, value: DOMString) {
        let owner = self.owner.root();
        let value = owner.parse_attribute(&self.namespace, self.local_name(), value);
        self.set_value(ReplacedAttr, value);
    }

    fn Name(self) -> DOMString {
        self.name.as_slice().to_string()
    }

    fn GetNamespaceURI(self) -> Option<DOMString> {
        let Namespace(ref atom) = self.namespace;
        match atom.as_slice() {
            "" => None,
            url => Some(url.to_string()),
        }
    }

    fn GetPrefix(self) -> Option<DOMString> {
        self.prefix.clone()
    }
}

pub trait AttrHelpers<'a> {
    fn set_value(self, set_type: AttrSettingType, value: AttrValue);
    fn value(self) -> Ref<'a, AttrValue>;
    fn local_name(self) -> &'a Atom;
    fn summarize(self) -> AttrInfo;
}

impl<'a> AttrHelpers<'a> for JSRef<'a, Attr> {
    fn set_value(self, set_type: AttrSettingType, value: AttrValue) {
        let owner = self.owner.root();
        let node: JSRef<Node> = NodeCast::from_ref(*owner);
        let namespace_is_null = self.namespace == ns!("");

        match set_type {
            ReplacedAttr => {
                if namespace_is_null {
                    vtable_for(&node).before_remove_attr(
                        self.local_name(),
                        self.value().as_slice().to_string())
                }
            }
            FirstSetAttr => {}
        }

        *self.value.borrow_mut() = value;

        if namespace_is_null {
            vtable_for(&node).after_set_attr(
                self.local_name(),
                self.value().as_slice().to_string())
        }
    }

    fn value(self) -> Ref<'a, AttrValue> {
        self.extended_deref().value.borrow()
    }

    fn local_name(self) -> &'a Atom {
        &self.extended_deref().local_name
    }

    fn summarize(self) -> AttrInfo {
        let Namespace(ref ns) = self.namespace;
        AttrInfo {
            namespace: ns.as_slice().to_string(),
            name: self.Name(),
            value: self.Value(),
        }
    }
}

pub trait AttrHelpersForLayout {
    unsafe fn value_ref_forever(&self) -> &'static str;
    unsafe fn value_atom_forever(&self) -> Option<Atom>;
    unsafe fn value_tokens_forever(&self) -> Option<&'static [Atom]>;
    unsafe fn local_name_atom_forever(&self) -> Atom;
}

impl AttrHelpersForLayout for Attr {
    #[inline]
    unsafe fn value_ref_forever(&self) -> &'static str {
        // This transmute is used to cheat the lifetime restriction.
        let value = mem::transmute::<&AttrValue, &AttrValue>(self.value.borrow_for_layout());
        value.as_slice()
    }

    #[inline]
    unsafe fn value_atom_forever(&self) -> Option<Atom> {
        let value = self.value.borrow_for_layout();
        match *value {
            AtomAttrValue(ref val) => Some(val.clone()),
            _ => None,
        }
    }

    #[inline]
    unsafe fn value_tokens_forever(&self) -> Option<&'static [Atom]> {
        // This transmute is used to cheat the lifetime restriction.
        let value = mem::transmute::<&AttrValue, &AttrValue>(self.value.borrow_for_layout());
        match *value {
            TokenListAttrValue(_, ref tokens) => Some(tokens.as_slice()),
            _ => None,
        }
    }

    #[inline]
    unsafe fn local_name_atom_forever(&self) -> Atom {
        self.local_name.clone()
    }
}
<|endoftext|>