<commit_before>use std::sync::{Mutex, Arc};
use std::net::{SocketAddr, ToSocketAddrs};
use std::f64::consts::PI;
use std::collections::VecDeque;
use opengl_graphics::GlGraphics;
use opengl_graphics::glyph_cache::GlyphCache;
use piston::input::{Button, Key, MouseButton, RenderArgs, UpdateArgs};

use std::{thread, time};
use std::net::TcpStream;
use network::{Command, Message};

use bincode::{serialize_into, deserialize_from, Infinite};

use state::WorldState;
use shapes::Unit;
use colors::{BLACK, YELLOW, ORANGE};

pub mod menu;

use self::menu::Menu;

pub struct NetworkClient {
    pub world_state: Arc<Mutex<WorldState>>,
    server_addr: SocketAddr,
    stream: Option<TcpStream>,
    commands: Arc<Mutex<VecDeque<Command>>>,
}

impl NetworkClient {
    pub fn new<T: ToSocketAddrs>(server_addrs: T, world_state:
                                 Arc<Mutex<WorldState>>,
                                 commands: Arc<Mutex<VecDeque<Command>>>) -> NetworkClient {
        let server_addr = server_addrs.to_socket_addrs().unwrap().next().unwrap();
        NetworkClient {
            world_state: world_state,
            server_addr: server_addr,
            stream: None,
            commands: commands,
        }
    }

    // todo: Maybe return client_id here? Would allow the application to reconnect...
    pub fn connect(&mut self) {
        let mut stream = TcpStream::connect(self.server_addr).unwrap();
        serialize_into(&mut stream, &Message::ClientHello, Infinite).unwrap();
        let server_hello = deserialize_from(&mut stream, Infinite);

        self.stream = Some(stream);
        if let Ok(Message::ServerHello(_, world_state)) = server_hello {
            let mut world_state_lock = self.world_state.lock().unwrap();
            *world_state_lock = world_state;
        } else {
            panic!("Could not connect to server");
        }
    }

    pub fn update(&self) {
        let stream = self.stream.as_ref().expect("Stream not here :(");
        let mut command_stream = stream.try_clone().unwrap();
        let commands = self.commands.clone();

        // Command sender loop
        thread::spawn(move || {
            loop {
                let command = {
                    let mut commands = commands.lock().unwrap();
                    commands.pop_front()
                };
                command.map(|cmd| {
                    println!("Got command: {:?}", cmd);
                    //let cmd = Message::Command(Command::Move(0.into(), [100f64, 100f64]));
                    serialize_into(&mut command_stream, &Message::Command(cmd), Infinite)
                        .unwrap_or_else(|e|println!("Sending command failed: {}", e));
                });
                thread::sleep(time::Duration::from_millis(10));
            }
        });

        let mut game_state_stream = stream.try_clone().unwrap();
        let world_state = self.world_state.clone();
        thread::spawn(move || {
            loop {
                let game_state = deserialize_from(&mut game_state_stream,
                                                  Infinite);
                match game_state {
                    Ok(game) => {
                        //println!("{:?}", game);
                        let mut world_state_lock = world_state.lock().unwrap();
                        world_state_lock.game = game;

                    }
                    Err(e) => {
                        println!("{:?}", e);
                        thread::sleep(time::Duration::from_millis(200));
                    }
                }
            }
        });
    }
}

#[derive(Clone, Copy, Debug)]
pub enum State {
    Menu,
    Running,
}

pub struct App {
    pub gl: GlGraphics, // OpenGL drawing backend.
    pub world_state: Arc<Mutex<WorldState>>,
    pub units: Vec<Unit>,
    pub commands: Arc<Mutex<VecDeque<Command>>>,
    pub cursor: [f64; 2],
    pub state: State,
    menu: Menu,
}

impl App {
    pub fn new(gl: GlGraphics) -> App {
        App {
            gl: gl,
            world_state: Arc::new(Mutex::new(WorldState::new(0, 0))),
            units: vec![],
            commands: Arc::new(Mutex::new(VecDeque::new())),
            cursor: [0.0, 0.0],
            state: State::Menu,
            menu: Menu::new()
        }
    }

    pub fn start(&mut self) {
        let mut network_client = NetworkClient::new(("127.0.0.1", 8080), self.world_state.clone(), self.commands.clone());
        network_client.connect();
        network_client.update();
    }

    pub fn select(&mut self, position: [f64;2]) {
        for u in &mut self.units {
            u.selected = u.is_hit(position);
        };
    }

    fn render_game(&mut self, args: &RenderArgs, _: &mut GlyphCache) {
        use graphics::{polygon, clear};
        use graphics::Transformed;
        use graphics::types::Polygon;

        const FRONT_THICKNESS: f64 = 5.0;

        let units = &self.units;

        self.gl.draw(args.viewport(), |c, gl| {

            // Clear the screen.
            clear(BLACK, gl);

            for s in units.iter() {

                // Create a triangle polygon. The initial orientation is facing east.
                let triangle: Polygon = &s.get_shape();

                // Create a border on the front of the polygon. This is a trapezoid.
                // Because the angle of the trapezoid side is 22.5°, we know that `dx` is always `2 * dy`.
                let front: Polygon = &[
                    [s.size, s.size],                                           // Top right
                    [s.size, 0.0],                                                 // Bottom right
                    [s.size - FRONT_THICKNESS, FRONT_THICKNESS / 2.0],             // Bottom left
                    [s.size - FRONT_THICKNESS, s.size - FRONT_THICKNESS / 2.0], // Top left
                ];

                // Rotate the front to match the unit
                let transform_front = c.transform.trans(s.position[0], s.position[1])
                    .rot_rad(s.rotation)
                    .trans(-25.0, -25.0);

                // We don't need to apply any transformation to the units
                let transform_triangle = c.transform;

                // Draw the unit ORANGE if selected
                if s.selected {
                    polygon(ORANGE, triangle, transform_triangle, gl);
                    polygon(YELLOW, front, transform_front, gl);
                } else {
                    polygon(YELLOW, triangle, transform_triangle, gl);
                    polygon(ORANGE, front, transform_front, gl);
                }

            }
        });
    }

    pub fn render(&mut self, args: &RenderArgs, cache: &mut GlyphCache) {
        match self.state {
            State::Menu => self.menu.render(args, &mut self.gl, cache), //self.render_menu(args, cache),
            State::Running => self.render_game(args, cache),
        }
    }

    pub fn update(&mut self, _: &UpdateArgs) {
        let player = {
            let world_lock = self.world_state.lock().unwrap();
            world_lock.game.players.get(0).map(|v| v.clone())
        };
        if let Some(player) = player {
            for unit in player.units.iter() {
                self.units.get_mut(unit.id.0 as usize)
                    .map(|app_unit| {
                        app_unit.position = unit.position;
                        app_unit.rotation = unit.angle;
                    })
                    .or_else(|| {
                        self.units.push(Unit::new(unit.position.clone(), unit.angle));
                        None
                    });
            }
        }
    }

    pub fn on_button_press(&mut self, button: &Button) {
        match self.state {
            State::Menu => {
                match button {
                    &Button::Keyboard(Key::Up) => {
                        self.menu.previous();
                    }
                    &Button::Keyboard(Key::Down) => {
                        self.menu.next();
                    }
                    &Button::Keyboard(Key::Return) => {
                        match self.menu.get_selected_entry() {
                            menu::Entries::Start => {
                                self.start();
                                self.state = State::Running;
                            }
                            menu::Entries::Exit => {
                            }
                        }
                    }
                    _ => { }
                }
            }
            State::Running => {
                match button {
                    &Button::Keyboard(_) => { }
                    &Button::Mouse(button) => {
                        self.on_mouse_click(&button);
                    }
                    &Button::Controller(_) => { }
                }
            }
        };
    }

    pub fn on_mouse_click(&mut self, button: &MouseButton) {
        let cursor = self.cursor;
        match *button {
            MouseButton::Left  => self.select(cursor),
            MouseButton::Right => self.move_selected(cursor),
            _ => println!("Pressed mouse button '{:?}'", button),
        }
    }

    pub fn on_mouse_move(&mut self, cursor: [f64; 2]) {
        self.cursor = cursor;
    }

    pub fn move_selected(&mut self, position: [f64;2]) {
        for i in 0..self.units.len() {
            let s = &mut self.units[i];
            if s.selected {
                s.target = position;
                let dx = position[0] - s.position[0];
                let dy = position[1] - s.position[1];
                if dx.is_sign_negative() {
                    s.rotation = (dy / dx).atan() + PI;
                } else {
                    s.rotation = (dy / dx).atan();
                }
                let id = i as u32;
                let mut commands = self.commands.lock().unwrap();
                commands.push_back(Command::Move(id.into(), position));
                println!("dx: {}, dy: {}, new rotation: {}", dx, dy, s.rotation);
            }
        }
    }
}
<commit_msg>Initial error handling on connect<commit_after>use std::sync::{Mutex, Arc};
use std::net::{SocketAddr, ToSocketAddrs};
use std::f64::consts::PI;
use std::collections::VecDeque;
use std::error::Error;
use opengl_graphics::GlGraphics;
use opengl_graphics::glyph_cache::GlyphCache;
use piston::input::{Button, Key, MouseButton, RenderArgs, UpdateArgs};

use std::{thread, time};
use std::net::TcpStream;
use network::{Command, Message};

use bincode::{serialize_into, deserialize_from, Infinite};

use state::{ClientId, WorldState};
use shapes::Unit;
use colors::{BLACK, YELLOW, ORANGE};

pub mod menu;

use self::menu::Menu;

pub struct NetworkClient {
    pub world_state: Arc<Mutex<WorldState>>,
    server_addr: SocketAddr,
    stream: Option<TcpStream>,
    commands: Arc<Mutex<VecDeque<Command>>>,
}

impl NetworkClient {
    pub fn new<T: ToSocketAddrs>(server_addrs: T, world_state:
                                 Arc<Mutex<WorldState>>,
                                 commands: Arc<Mutex<VecDeque<Command>>>) -> NetworkClient {
        let server_addr = server_addrs.to_socket_addrs().unwrap().next().unwrap();
        NetworkClient {
            world_state: world_state,
            server_addr: server_addr,
            stream: None,
            commands: commands,
        }
    }

    // todo: Maybe return client_id here? Would allow the application to reconnect...
    pub fn connect(&mut self) -> Result<ClientId, Box<Error>>  {
        let mut stream = TcpStream::connect(self.server_addr)?;
        serialize_into(&mut stream, &Message::ClientHello, Infinite)?;
        let server_hello = deserialize_from(&mut stream, Infinite);

        self.stream = Some(stream);
        if let Ok(Message::ServerHello(_, world_state)) = server_hello {
            let mut world_state_lock = self.world_state.lock().unwrap();
            *world_state_lock = world_state;
        } else {
            panic!("Could not connect to server");
        }
        Ok(0.into())
    }

    pub fn update(&self) {
        let stream = self.stream.as_ref().expect("Stream not here :(");
        let mut command_stream = stream.try_clone().unwrap();
        let commands = self.commands.clone();

        // Command sender loop
        thread::spawn(move || {
            loop {
                let command = {
                    let mut commands = commands.lock().unwrap();
                    commands.pop_front()
                };
                command.map(|cmd| {
                    println!("Got command: {:?}", cmd);
                    //let cmd = Message::Command(Command::Move(0.into(), [100f64, 100f64]));
                    serialize_into(&mut command_stream, &Message::Command(cmd), Infinite)
                        .unwrap_or_else(|e|println!("Sending command failed: {}", e));
                });
                thread::sleep(time::Duration::from_millis(10));
            }
        });

        let mut game_state_stream = stream.try_clone().unwrap();
        let world_state = self.world_state.clone();
        thread::spawn(move || {
            loop {
                let game_state = deserialize_from(&mut game_state_stream,
                                                  Infinite);
                match game_state {
                    Ok(game) => {
                        //println!("{:?}", game);
                        let mut world_state_lock = world_state.lock().unwrap();
                        world_state_lock.game = game;

                    }
                    Err(e) => {
                        println!("{:?}", e);
                        thread::sleep(time::Duration::from_millis(200));
                    }
                }
            }
        });
    }
}

#[derive(Clone, Copy, Debug)]
pub enum State {
    Menu,
    Running,
}

pub struct App {
    pub gl: GlGraphics, // OpenGL drawing backend.
    pub world_state: Arc<Mutex<WorldState>>,
    pub units: Vec<Unit>,
    pub commands: Arc<Mutex<VecDeque<Command>>>,
    pub cursor: [f64; 2],
    pub state: State,
    menu: Menu,
    client_id: Option<ClientId>,
}

impl App {
    pub fn new(gl: GlGraphics) -> App {
        App {
            gl: gl,
            world_state: Arc::new(Mutex::new(WorldState::new(0, 0))),
            units: vec![],
            commands: Arc::new(Mutex::new(VecDeque::new())),
            cursor: [0.0, 0.0],
            state: State::Menu,
            menu: Menu::new(),
            client_id: None,
        }
    }

    pub fn start(&mut self) -> Result<(), Box<Error>> {
        let mut network_client = NetworkClient::new(("127.0.0.1", 8080), self.world_state.clone(), self.commands.clone());
        self.client_id = Some(network_client.connect()?);
        network_client.update();
        Ok(())
    }

    pub fn select(&mut self, position: [f64;2]) {
        for u in &mut self.units {
            u.selected = u.is_hit(position);
        };
    }

    fn render_game(&mut self, args: &RenderArgs, _: &mut GlyphCache) {
        use graphics::{polygon, clear};
        use graphics::Transformed;
        use graphics::types::Polygon;

        const FRONT_THICKNESS: f64 = 5.0;

        let units = &self.units;

        self.gl.draw(args.viewport(), |c, gl| {

            // Clear the screen.
            clear(BLACK, gl);

            for s in units.iter() {

                // Create a triangle polygon. The initial orientation is facing east.
                let triangle: Polygon = &s.get_shape();

                // Create a border on the front of the polygon. This is a trapezoid.
                // Because the angle of the trapezoid side is 22.5°, we know that `dx` is always `2 * dy`.
                let front: Polygon = &[
                    [s.size, s.size],                                           // Top right
                    [s.size, 0.0],                                                 // Bottom right
                    [s.size - FRONT_THICKNESS, FRONT_THICKNESS / 2.0],             // Bottom left
                    [s.size - FRONT_THICKNESS, s.size - FRONT_THICKNESS / 2.0], // Top left
                ];

                // Rotate the front to match the unit
                let transform_front = c.transform.trans(s.position[0], s.position[1])
                    .rot_rad(s.rotation)
                    .trans(-25.0, -25.0);

                // We don't need to apply any transformation to the units
                let transform_triangle = c.transform;

                // Draw the unit ORANGE if selected
                if s.selected {
                    polygon(ORANGE, triangle, transform_triangle, gl);
                    polygon(YELLOW, front, transform_front, gl);
                } else {
                    polygon(YELLOW, triangle, transform_triangle, gl);
                    polygon(ORANGE, front, transform_front, gl);
                }

            }
        });
    }

    pub fn render(&mut self, args: &RenderArgs, cache: &mut GlyphCache) {
        match self.state {
            State::Menu => self.menu.render(args, &mut self.gl, cache), //self.render_menu(args, cache),
            State::Running => self.render_game(args, cache),
        }
    }

    pub fn update(&mut self, _: &UpdateArgs) {
        let player = {
            let world_lock = self.world_state.lock().unwrap();
            world_lock.game.players.get(0).map(|v| v.clone())
        };
        if let Some(player) = player {
            for unit in player.units.iter() {
                self.units.get_mut(unit.id.0 as usize)
                    .map(|app_unit| {
                        app_unit.position = unit.position;
                        app_unit.rotation = unit.angle;
                    })
                    .or_else(|| {
                        self.units.push(Unit::new(unit.position.clone(), unit.angle));
                        None
                    });
            }
        }
    }

    pub fn on_button_press(&mut self, button: &Button) {
        match self.state {
            State::Menu => {
                match button {
                    &Button::Keyboard(Key::Up) => {
                        self.menu.previous();
                    }
                    &Button::Keyboard(Key::Down) => {
                        self.menu.next();
                    }
                    &Button::Keyboard(Key::Return) => {
                        match self.menu.get_selected_entry() {
                            menu::Entries::Start => {
                                // TODO: Proper error handling
                                if self.start().is_ok() {
                                    self.state = State::Running;
                                }
                            }
                            menu::Entries::Exit => {
                            }
                        }
                    }
                    _ => { }
                }
            }
            State::Running => {
                match button {
                    &Button::Keyboard(_) => { }
                    &Button::Mouse(button) => {
                        self.on_mouse_click(&button);
                    }
                    &Button::Controller(_) => { }
                }
            }
        };
    }

    pub fn on_mouse_click(&mut self, button: &MouseButton) {
        let cursor = self.cursor;
        match *button {
            MouseButton::Left  => self.select(cursor),
            MouseButton::Right => self.move_selected(cursor),
            _ => println!("Pressed mouse button '{:?}'", button),
        }
    }

    pub fn on_mouse_move(&mut self, cursor: [f64; 2]) {
        self.cursor = cursor;
    }

    pub fn move_selected(&mut self, position: [f64;2]) {
        for i in 0..self.units.len() {
            let s = &mut self.units[i];
            if s.selected {
                s.target = position;
                let dx = position[0] - s.position[0];
                let dy = position[1] - s.position[1];
                if dx.is_sign_negative() {
                    s.rotation = (dy / dx).atan() + PI;
                } else {
                    s.rotation = (dy / dx).atan();
                }
                let id = i as u32;
                let mut commands = self.commands.lock().unwrap();
                commands.push_back(Command::Move(id.into(), position));
                println!("dx: {}, dy: {}, new rotation: {}", dx, dy, s.rotation);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io::BufRead;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Write;
use std::ops::DerefMut;
use std::str;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use rand::thread_rng;
use rand::Rng;
use rand::ThreadRng;

use std::net::TcpStream;
use rustc_serialize::json;

use Result;
use config::Config;
use errors::Error;
use message::Message;
use parse::MessageArg;
use parse::Parser;
use subscription::new_channel_subscription;
use subscription::new_async_subscription;
use subscription::AsyncSubscription;
use subscription::ChannelSubscription;
use subscription::DispatchMessage;
use subscription::Subscription;
use subscription::SubscriptionID;

// TODO TLS
// TODO Need to figure out flushing
// TODO need to add reconnects
// #[derive(Debug)]
pub struct NatsCoreConn {
    stream: BufWriter<TcpStream>,
    subscriptions: HashMap<u64, Subscription>,
    server_info: NatsServerInfo,
}

// TODO another trait for message transmission?

pub trait MessageProcessor {
    fn process_ok(&mut self);
    fn process_err(&mut self, message: &[u8]);
    fn process_ping(&mut self);
    fn process_pong(&mut self);
    fn process_message(&mut self, args: &MessageArg, message: &[u8]);
}

impl NatsCoreConn {
    pub fn new(config: &Config) -> Result<(BufReader<TcpStream>, NatsCoreConn)> {
        let stream = try!(NatsCoreConn::create_tcp_stream(config));
        let mut reader = BufReader::new(try!(stream.try_clone()));

        let mut s = "".to_string();
        try!(reader.read_line(&mut s));
        // if s[..5] != "INFO " {
        //     println!("{}", s);
        //     return Err(Error::ParseError);
        // }
        // TODO handle this better
        let server_info: NatsServerInfo = json::decode(&s[5..]).unwrap();

        let mut conn = NatsCoreConn {
            subscriptions: HashMap::new(),
            stream: BufWriter::new(stream),
            server_info: server_info,
        };
        try!(conn.connect(&mut reader, config));

        Ok((reader, conn))
    }

    fn create_tcp_stream(config: &Config) -> Result<TcpStream> {
        for host in &config.hosts {
            if let Ok(stream) = TcpStream::connect(host) {
                return Ok(stream);
            }
        }

        return Err(Error::NoServers);
    }

    // Protocol is "CONNECT <json options>"
    fn connect(&mut self, reader: &mut BufReader<TcpStream>, config: &Config) -> Result<()> {
        // TODO need to parse the host
        let conn_info = NatsConnInfo {
            verbose: config.verbose,
            pedantic: config.pedantic,
            user: None,
            pass: None,
            auth_token: None,
            ssl_required: false,
            name: String::from("TODO"),
            lang: String::from("rust"),
            version: String::from("0.1.0"),
        };

        // TODO set read timeouts?
        let conn_message = format!("CONNECT {}\r\n", json::encode(&conn_info).unwrap());
        try!(self.stream.write_all(conn_message.as_bytes()));
        try!(self.stream.flush());

        // TODO fix
        if config.verbose {
            let mut response = "".to_string();
            try!(reader.read_line(&mut response));
            if response != "+OK\r\n" {
                return Err(Error::ParseError);
            }
        }

        // send a ping message and expect a PONG
        try!(self.ping());
        try!(self.stream.flush());

        let mut s = "".to_string();
        try!(reader.read_line(&mut s));
        if s != "PONG\r\n" {
            return Err(Error::ParseError);
        }

        return Ok(());
    }

    pub fn close(&mut self) -> Result<()> {
        try!(self.stream.flush());
        Ok(())
    }

    pub fn flush(&mut self) -> Result<()> {
        try!(self.stream.flush());
        Ok(())
    }

    pub fn ping(&mut self) -> Result<()> {
        try!(self.stream.write_all("PING\r\n".as_bytes()));
        Ok(())
    }

    pub fn pong(&mut self) -> Result<()> {
        try!(self.stream.write_all("PONG\r\n".as_bytes()));
        Ok(())
    }

    // Protocol is "PUB <subject> [reply-to] <payload size>\r\n[payload]\r\n"
    pub fn publish(&mut self, subject: &str, reply: Option<&str>, data: &[u8]) -> Result<()> {
        // TODO need probably a better was of creating messages
        if data.len() as u64 > self.server_info.max_payload {
            return Err(Error::MessageTooLarge);
        }

        let mut buf = Vec::<u8>::new();
        buf.extend("PUB ".as_bytes());
        buf.extend(subject.as_bytes());
        buf.extend(" ".as_bytes());
        match reply {
            Some(s) => {
                buf.extend(s.as_bytes());
                buf.extend(" ".as_bytes());
            },
            None => (),
        };

        if data.len() == 0 {
            buf.extend("0".as_bytes());
        } else {
            buf.extend(format!("{}", data.len()).as_bytes());
        }
        buf.extend("\r\n".as_bytes());

        if data.len() > 0 {
            buf.extend(data);
        }
        buf.extend("\r\n".as_bytes());

        trace!("publishing a message to {}", subject);
        try!(self.stream.write_all(&buf));
        Ok(())
    }

    pub fn subscribe(&mut self, subject: &str, group: Option<&str>, subscription: Subscription) -> Result<()> {
        let sid = subscription.id;
        self.subscriptions.insert(sid, subscription);
        let sub_message = format!("SUB {} {} {}\r\n", subject, group.unwrap_or(""), sid);
        trace!("subscribing to {}", subject);
        try!(self.stream.write_all(sub_message.as_bytes()));
        Ok(())
    }

    pub fn unsubscribe<S: SubscriptionID>(&mut self, subscription: &S, max: Option<usize>) -> Result<()> {
        let mut max_str = String::from("");

        match max {
            Some(n) => {
                max_str = format!("{}", n);
                if let Some(sub) = self.subscriptions.get_mut(&subscription.sub_id()) {
                    (*sub).max = max;
                }
            },
            None => {
                self.subscriptions.remove(&subscription.sub_id());
            },
        };

        trace!("unsubscribing to {}", subscription.sub_id());
        let unsub_message = format!("UNSUB {} {}", subscription.sub_id(), max_str);
        try!(self.stream.write_all(unsub_message.as_bytes()));
        Ok(())
    }
}

impl MessageProcessor for NatsCoreConn {
    fn process_ok(&mut self) {
        trace!("received +OK");
    }

    fn process_err(&mut self, message: &[u8]) {
        error!("received error from server, closing connection: {}", str::from_utf8(message).unwrap());
        // TODO close connection/reconnect?
    }

    fn process_ping(&mut self) {
        // TODO send pong
        trace!("received PING");
        self.pong().unwrap();
    }

    fn process_pong(&mut self) {
        // TODO
        trace!("received PONG");
    }

    fn process_message(&mut self, args: &MessageArg, message: &[u8]) {
        // TODO this is REALLY ugly
        let max: Option<usize>;
        let delivered: usize;

        {
            let sub = self.subscriptions.get_mut(&args.sid);
            if sub.is_none() {
                return;
            }
            let mut s = sub.unwrap();
            s.delivered += 1;
            max = s.max;
            delivered = s.delivered;

            let mut data = Vec::with_capacity(message.len());
            data.extend_from_slice(message);
            let m = Message {
                // TODO efficiency
                subject: String::from(str::from_utf8(&args.subject).unwrap()),
                reply: args.reply.as_ref().map(|s| String::from(str::from_utf8(s).unwrap())),
                data: data,
            };
            // TODO error
            (*s.dispatcher).dispatch_message(m).unwrap();
        }

        if let Some(m) = max {
            if delivered >= m {
                self.subscriptions.remove(&args.sid);
            }
        }
    }
}

// #[derive(Debug)]
pub struct NatsConn {
    // config: Config,
    core_conn: Arc<Mutex<NatsCoreConn>>,
    next_sid: u64,
    rng: ThreadRng,
}

impl NatsConn {
    pub fn new(config: Config) -> Result<NatsConn> {
        let (reader, core_conn) = try!(NatsCoreConn::new(&config));
        let conn = NatsConn {
            core_conn: Arc::new(Mutex::new(core_conn)),
            next_sid: 0,
            rng: thread_rng(),
        };

        let core_conn_clone = conn.core_conn.clone();
        thread::spawn(move || {
            NatsConn::read_loop(core_conn_clone, reader);
        });

        Ok(conn)
    }

    fn read_loop(core_conn: Arc<Mutex<NatsCoreConn>>, mut reader: BufReader<TcpStream>) {
        let mut parser = Parser::new();
        let mut buf: [u8; 32768] = [0; 32768];
        loop {
            // TODO
            match reader.read(&mut buf) {
                Ok(n) => {
                    let mut c = core_conn.lock().unwrap();
                    parser.parse(c.deref_mut(), &buf[..n]).unwrap();
                },
                Err(_) => {
                    return;
                },
            };
        }
    }

    pub fn close(&mut self) -> Result<()> {
        try!(self.core_conn.lock().unwrap().close());
        Ok(())
    }

    pub fn publish(&mut self, subject: &str, reply: Option<&str>, data: &[u8]) -> Result<()> {
        self.core_conn.lock().unwrap().publish(subject, reply, data)
    }

    pub fn publish_message(&mut self, message: Message) -> Result<()> {
        self.core_conn.lock().unwrap().publish(&message.subject, message.reply.as_ref().map(|s| s.as_str()), &message.data)
    }

    fn new_inbox(&mut self) -> String {
        String::from("_INBOX.") + self.rng.gen_ascii_chars().take(22).collect::<String>().as_str()
    }

    pub fn request(&mut self, subject: &str, data: &[u8]) -> Result<Message> {
        let inbox = self.new_inbox();
        let sub = try!(self.subscribe_channel(subject, None));
        let mut core_conn = self.core_conn.lock().unwrap();
        try!(core_conn.unsubscribe(&sub, Some(1)));
        try!(core_conn.publish(subject, Some(&inbox), data));
        // TODO timeout? select! call requires nightly
        Ok(try!(sub.receiver.recv()))
    }

    pub fn subscribe_channel(&mut self, subject: &str, group: Option<&str>) -> Result<ChannelSubscription> {
        let sid = self.next_sid;
        self.next_sid += 1;
        let (send_sub, recv_sub) = new_channel_subscription(sid);
        try!(self.core_conn.lock().unwrap().subscribe(subject, group, send_sub));
        Ok(recv_sub)
    }

    pub fn subscribe_async<F>(&mut self, callback: F, subject: &str, group: Option<&str>) -> Result<AsyncSubscription> where F: Fn(Message) + Send + 'static{
        let sid = self.next_sid;
        self.next_sid += 1;
        let (send_sub, recv_sub) = new_async_subscription(sid, callback);
        try!(self.core_conn.lock().unwrap().subscribe(subject, group, send_sub));
        Ok(recv_sub)
    }

    pub fn unsubscribe<S: SubscriptionID>(&mut self, subscription: &S) -> Result<()> {
        self.core_conn.lock().unwrap().unsubscribe(subscription, None)
    }

    pub fn auto_unsubscribe<S: SubscriptionID>(&mut self, subscription: &S, max: usize) -> Result<()> {
        self.core_conn.lock().unwrap().unsubscribe(subscription, Some(max))
    }

    pub fn flush(&mut self) -> Result<()> {
        self.core_conn.lock().unwrap().flush()
    }
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
struct NatsConnInfo {
    pub verbose: bool,
    pub pedantic: bool,
    pub user: Option<String>,
    pub pass: Option<String>,
    pub auth_token: Option<String>,
    pub ssl_required: bool,
    pub name: String,
    pub lang: String,
    pub version: String,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
struct NatsServerInfo {
    pub server_id: String,
    pub host: String,
    pub port: usize,
    pub version: String,
    pub auth_required: bool,
    pub tls_required: bool,
    pub max_payload: u64,
}
//
// struct NatsConnHandler {
//     core_conn: Arc<Mutex<NatsCoreConn>>,
//     parser: Parser,
//     // TODO what size to use?
//     buf: [u8; 32768],
// }
//
// impl NatsConnHandler {
//     pub fn new(core_conn: Arc<Mutex<NatsCoreConn>>) -> NatsConnHandler {
//         NatsConnHandler {
//             core_conn: core_conn,
//             parser: Parser::new(),
//             buf: [0; 32768],
//         }
//     }
// }
//
// impl Handler for NatsConnHandler {
//     type Timeout = ();
//     type Message = ();
//
//     fn ready(&mut self, _event_loop: &mut EventLoop<NatsConnHandler>, _token: Token, events: EventSet) {
//         // should only be receiving readable events? only one connection
//         if !events.is_readable() {
//             return;
//         }
//
//         let mut core_conn = self.core_conn.lock().unwrap();
//         match core_conn.stream.read(&mut self.buf) {
//             Ok(n) => {
//                 // TODO why doesn't this coerce automatically?
//                 self.parser.parse(core_conn.deref_mut(), &self.buf[..n]).unwrap();
//             },
//             Err(_) => (),
//         };
//     }
//
//     fn notify(&mut self, event_loop: &mut EventLoop<Self>, _message: ()) {
//         // Getting a message means we should shutdown
//         event_loop.shutdown();
//     }
// }
<commit_msg>remove some commented out code<commit_after>use std::collections::HashMap;
use std::io::BufRead;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Write;
use std::ops::DerefMut;
use std::str;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use rand::thread_rng;
use rand::Rng;
use rand::ThreadRng;

use std::net::TcpStream;
use rustc_serialize::json;

use Result;
use config::Config;
use errors::Error;
use message::Message;
use parse::MessageArg;
use parse::Parser;
use subscription::new_channel_subscription;
use subscription::new_async_subscription;
use subscription::AsyncSubscription;
use subscription::ChannelSubscription;
use subscription::DispatchMessage;
use subscription::Subscription;
use subscription::SubscriptionID;

// TODO TLS
// TODO Need to figure out flushing
// TODO need to add reconnects
// #[derive(Debug)]
pub struct NatsCoreConn {
    stream: BufWriter<TcpStream>,
    subscriptions: HashMap<u64, Subscription>,
    server_info: NatsServerInfo,
}

// TODO another trait for message transmission?

pub trait MessageProcessor {
    fn process_ok(&mut self);
    fn process_err(&mut self, message: &[u8]);
    fn process_ping(&mut self);
    fn process_pong(&mut self);
    fn process_message(&mut self, args: &MessageArg, message: &[u8]);
}

impl NatsCoreConn {
    pub fn new(config: &Config) -> Result<(BufReader<TcpStream>, NatsCoreConn)> {
        let stream = try!(NatsCoreConn::create_tcp_stream(config));
        let mut reader = BufReader::new(try!(stream.try_clone()));

        let mut s = "".to_string();
        try!(reader.read_line(&mut s));
        if &s[..5] != "INFO " {
            return Err(Error::ParseError);
        }
        // TODO handle this better
        let server_info: NatsServerInfo = json::decode(&s[5..]).unwrap();

        let mut conn = NatsCoreConn {
            subscriptions: HashMap::new(),
            stream: BufWriter::new(stream),
            server_info: server_info,
        };
        try!(conn.connect(&mut reader, config));

        Ok((reader, conn))
    }

    fn create_tcp_stream(config: &Config) -> Result<TcpStream> {
        for host in &config.hosts {
            if let Ok(stream) = TcpStream::connect(host) {
                return Ok(stream);
            }
        }

        return Err(Error::NoServers);
    }

    // Protocol is "CONNECT <json options>"
    fn connect(&mut self, reader: &mut BufReader<TcpStream>, config: &Config) -> Result<()> {
        // TODO need to parse the host
        let conn_info = NatsConnInfo {
            verbose: config.verbose,
            pedantic: config.pedantic,
            user: None,
            pass: None,
            auth_token: None,
            ssl_required: false,
            name: String::from("TODO"),
            lang: String::from("rust"),
            version: String::from("0.1.0"),
        };

        // TODO set read timeouts?
        let conn_message = format!("CONNECT {}\r\n", json::encode(&conn_info).unwrap());
        try!(self.stream.write_all(conn_message.as_bytes()));
        try!(self.stream.flush());

        // TODO fix
        if config.verbose {
            let mut response = "".to_string();
            try!(reader.read_line(&mut response));
            if response != "+OK\r\n" {
                return Err(Error::ParseError);
            }
        }

        // send a ping message and expect a PONG
        try!(self.ping());
        try!(self.stream.flush());

        let mut s = "".to_string();
        try!(reader.read_line(&mut s));
        if s != "PONG\r\n" {
            return Err(Error::ParseError);
        }

        return Ok(());
    }

    pub fn close(&mut self) -> Result<()> {
        try!(self.stream.flush());
        Ok(())
    }

    pub fn flush(&mut self) -> Result<()> {
        try!(self.stream.flush());
        Ok(())
    }

    pub fn ping(&mut self) -> Result<()> {
        try!(self.stream.write_all("PING\r\n".as_bytes()));
        Ok(())
    }

    pub fn pong(&mut self) -> Result<()> {
        try!(self.stream.write_all("PONG\r\n".as_bytes()));
        Ok(())
    }

    // Protocol is "PUB <subject> [reply-to] <payload size>\r\n[payload]\r\n"
    pub fn publish(&mut self, subject: &str, reply: Option<&str>, data: &[u8]) -> Result<()> {
        // TODO need probably a better was of creating messages
        if data.len() as u64 > self.server_info.max_payload {
            return Err(Error::MessageTooLarge);
        }

        let mut buf = Vec::<u8>::new();
        buf.extend("PUB ".as_bytes());
        buf.extend(subject.as_bytes());
        buf.extend(" ".as_bytes());
        match reply {
            Some(s) => {
                buf.extend(s.as_bytes());
                buf.extend(" ".as_bytes());
            },
            None => (),
        };

        if data.len() == 0 {
            buf.extend("0".as_bytes());
        } else {
            buf.extend(format!("{}", data.len()).as_bytes());
        }
        buf.extend("\r\n".as_bytes());

        if data.len() > 0 {
            buf.extend(data);
        }
        buf.extend("\r\n".as_bytes());

        trace!("publishing a message to {}", subject);
        try!(self.stream.write_all(&buf));
        Ok(())
    }

    pub fn subscribe(&mut self, subject: &str, group: Option<&str>, subscription: Subscription) -> Result<()> {
        let sid = subscription.id;
        self.subscriptions.insert(sid, subscription);
        let sub_message = format!("SUB {} {} {}\r\n", subject, group.unwrap_or(""), sid);
        trace!("subscribing to {}", subject);
        try!(self.stream.write_all(sub_message.as_bytes()));
        Ok(())
    }

    pub fn unsubscribe<S: SubscriptionID>(&mut self, subscription: &S, max: Option<usize>) -> Result<()> {
        let mut max_str = String::from("");

        match max {
            Some(n) => {
                max_str = format!("{}", n);
                if let Some(sub) = self.subscriptions.get_mut(&subscription.sub_id()) {
                    (*sub).max = max;
                }
            },
            None => {
                self.subscriptions.remove(&subscription.sub_id());
            },
        };

        trace!("unsubscribing to {}", subscription.sub_id());
        let unsub_message = format!("UNSUB {} {}", subscription.sub_id(), max_str);
        try!(self.stream.write_all(unsub_message.as_bytes()));
        Ok(())
    }
}

impl MessageProcessor for NatsCoreConn {
    fn process_ok(&mut self) {
        trace!("received +OK");
    }

    fn process_err(&mut self, message: &[u8]) {
        error!("received error from server, closing connection: {}", str::from_utf8(message).unwrap());
        // TODO close connection/reconnect?
    }

    fn process_ping(&mut self) {
        // TODO send pong
        trace!("received PING");
        self.pong().unwrap();
    }

    fn process_pong(&mut self) {
        // TODO
        trace!("received PONG");
    }

    fn process_message(&mut self, args: &MessageArg, message: &[u8]) {
        // TODO this is REALLY ugly
        let max: Option<usize>;
        let delivered: usize;

        {
            let sub = self.subscriptions.get_mut(&args.sid);
            if sub.is_none() {
                return;
            }
            let mut s = sub.unwrap();
            s.delivered += 1;
            max = s.max;
            delivered = s.delivered;

            let mut data = Vec::with_capacity(message.len());
            data.extend_from_slice(message);
            let m = Message {
                // TODO efficiency
                subject: String::from(str::from_utf8(&args.subject).unwrap()),
                reply: args.reply.as_ref().map(|s| String::from(str::from_utf8(s).unwrap())),
                data: data,
            };
            // TODO error
            (*s.dispatcher).dispatch_message(m).unwrap();
        }

        if let Some(m) = max {
            if delivered >= m {
                self.subscriptions.remove(&args.sid);
            }
        }
    }
}

// #[derive(Debug)]
pub struct NatsConn {
    // config: Config,
    core_conn: Arc<Mutex<NatsCoreConn>>,
    next_sid: u64,
    rng: ThreadRng,
}

impl NatsConn {
    pub fn new(config: Config) -> Result<NatsConn> {
        let (reader, core_conn) = try!(NatsCoreConn::new(&config));
        let conn = NatsConn {
            core_conn: Arc::new(Mutex::new(core_conn)),
            next_sid: 0,
            rng: thread_rng(),
        };

        let core_conn_clone = conn.core_conn.clone();
        thread::spawn(move || {
            NatsConn::read_loop(core_conn_clone, reader);
        });

        Ok(conn)
    }

    fn read_loop(core_conn: Arc<Mutex<NatsCoreConn>>, mut reader: BufReader<TcpStream>) {
        let mut parser = Parser::new();
        // TODO check size
        let mut buf: [u8; 32768] = [0; 32768];
        loop {
            match reader.read(&mut buf) {
                Ok(n) => {
                    let mut c = core_conn.lock().unwrap();
                    parser.parse(c.deref_mut(), &buf[..n]).unwrap();
                },
                Err(_) => {
                    return;
                },
            };
        }
    }

    pub fn close(&mut self) -> Result<()> {
        try!(self.core_conn.lock().unwrap().close());
        Ok(())
    }

    pub fn publish(&mut self, subject: &str, reply: Option<&str>, data: &[u8]) -> Result<()> {
        self.core_conn.lock().unwrap().publish(subject, reply, data)
    }

    pub fn publish_message(&mut self, message: Message) -> Result<()> {
        self.core_conn.lock().unwrap().publish(&message.subject, message.reply.as_ref().map(|s| s.as_str()), &message.data)
    }

    fn new_inbox(&mut self) -> String {
        String::from("_INBOX.") + self.rng.gen_ascii_chars().take(22).collect::<String>().as_str()
    }

    pub fn request(&mut self, subject: &str, data: &[u8]) -> Result<Message> {
        let inbox = self.new_inbox();
        let sub = try!(self.subscribe_channel(subject, None));
        let mut core_conn = self.core_conn.lock().unwrap();
        try!(core_conn.unsubscribe(&sub, Some(1)));
        try!(core_conn.publish(subject, Some(&inbox), data));
        // TODO timeout? select! call requires nightly
        Ok(try!(sub.receiver.recv()))
    }

    pub fn subscribe_channel(&mut self, subject: &str, group: Option<&str>) -> Result<ChannelSubscription> {
        let sid = self.next_sid;
        self.next_sid += 1;
        let (send_sub, recv_sub) = new_channel_subscription(sid);
        try!(self.core_conn.lock().unwrap().subscribe(subject, group, send_sub));
        Ok(recv_sub)
    }

    pub fn subscribe_async<F>(&mut self, callback: F, subject: &str, group: Option<&str>) -> Result<AsyncSubscription> where F: Fn(Message) + Send + 'static{
        let sid = self.next_sid;
        self.next_sid += 1;
        let (send_sub, recv_sub) = new_async_subscription(sid, callback);
        try!(self.core_conn.lock().unwrap().subscribe(subject, group, send_sub));
        Ok(recv_sub)
    }

    pub fn unsubscribe<S: SubscriptionID>(&mut self, subscription: &S) -> Result<()> {
        self.core_conn.lock().unwrap().unsubscribe(subscription, None)
    }

    pub fn auto_unsubscribe<S: SubscriptionID>(&mut self, subscription: &S, max: usize) -> Result<()> {
        self.core_conn.lock().unwrap().unsubscribe(subscription, Some(max))
    }

    pub fn flush(&mut self) -> Result<()> {
        self.core_conn.lock().unwrap().flush()
    }
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
struct NatsConnInfo {
    pub verbose: bool,
    pub pedantic: bool,
    pub user: Option<String>,
    pub pass: Option<String>,
    pub auth_token: Option<String>,
    pub ssl_required: bool,
    pub name: String,
    pub lang: String,
    pub version: String,
}

#[derive(Debug, RustcDecodable, RustcEncodable)]
struct NatsServerInfo {
    pub server_id: String,
    pub host: String,
    pub port: usize,
    pub version: String,
    pub auth_required: bool,
    pub tls_required: bool,
    pub max_payload: u64,
}
<|endoftext|>


<commit_before>use std::slice::bytes::copy_memory;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static fontset: [[u8, .. 5], .. 0x10] = [
    [0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
    [0x20, 0x60, 0x20, 0x20, 0x70], // 1
    [0xF0, 0x10, 0xF0, 0x80, 0xF0], // 2
    [0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
    [0x90, 0x90, 0xF0, 0x10, 0x10], // 4
    [0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
    [0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
    [0xF0, 0x10, 0x20, 0x40, 0x40], // 7
    [0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
    [0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
    [0xF0, 0x90, 0xF0, 0x90, 0x90], // A
    [0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
    [0xF0, 0x80, 0x80, 0x80, 0xF0], // C
    [0xE0, 0x90, 0x90, 0x90, 0xE0], // D
    [0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
    [0xF0, 0x80, 0xF0, 0x80, 0x80], // F
];

type DrawCallback<'a> = |pixels: &[u8], w: uint, h: uint|: 'a;

pub struct Chip8<'a> {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
    draw_callback: DrawCallback<'a>
}

impl <'a> Chip8 <'a> {

    pub fn new(draw_callback: DrawCallback<'a>) -> Chip8<'a> {
        Chip8 {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
            draw_callback: draw_callback
        }
    }

    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.mut_slice(START_ADDR as uint, len), rom);
    }

    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:x}", ins)
        }
    }

    fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        b1 as u16 << 8 | b2 as u16
    }

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }

        (self.draw_callback)(self.display, DISPLAY_WIDTH, DISPLAY_HEIGHT);
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }
}
<commit_msg>Add documentation<commit_after>//! CHIP-8 emulator backend.
//! You can develop your own frontends for it.
//!
//! The reference frontend is
//! [crusty-chip-sfml](https://github.com/crumblingstatue/crusty-chip-sfml).

use std::slice::bytes::copy_memory;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static fontset: [[u8, .. 5], .. 0x10] = [
    [0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
    [0x20, 0x60, 0x20, 0x20, 0x70], // 1
    [0xF0, 0x10, 0xF0, 0x80, 0xF0], // 2
    [0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
    [0x90, 0x90, 0xF0, 0x10, 0x10], // 4
    [0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
    [0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
    [0xF0, 0x10, 0x20, 0x40, 0x40], // 7
    [0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
    [0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
    [0xF0, 0x90, 0xF0, 0x90, 0x90], // A
    [0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
    [0xF0, 0x80, 0x80, 0x80, 0xF0], // C
    [0xE0, 0x90, 0x90, 0x90, 0xE0], // D
    [0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
    [0xF0, 0x80, 0xF0, 0x80, 0x80], // F
];

type DrawCallback<'a> = |pixels: &[u8], w: uint, h: uint|: 'a;

/// CHIP-8 virtual machine
pub struct Chip8<'a> {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
    draw_callback: DrawCallback<'a>
}

impl <'a> Chip8 <'a> {

    /// Create a new Chip8 VM
    ///
    /// ## Arguments ##
    /// * draw_callback - Callback used when drawing
    pub fn new(draw_callback: DrawCallback<'a>) -> Chip8<'a> {
        Chip8 {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
            draw_callback: draw_callback
        }
    }

    /// Load a ROM
    ///
    /// ## Arguments ##
    /// * rom - ROM to load
    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.mut_slice(START_ADDR as uint, len), rom);
    }

    /// Do an emulation cycle.
    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:x}", ins)
        }
    }

    fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        b1 as u16 << 8 | b2 as u16
    }

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }

        (self.draw_callback)(self.display, DISPLAY_WIDTH, DISPLAY_HEIGHT);
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }
}
<|endoftext|>


<commit_before>extern crate serde_json;

use self::super::{ERR_HTSI, ERR_INVALID, MAP, OK};
use self::super::super::http;

use base64::{encode, decode};
use std::slice;
use std::u16;
use std::u8;

#[derive(Serialize)]
struct RequestData {
    dad: u8,
    sad: u8,
    lenc: u16,
    command: String,
    lenr: u16,
}

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct ResponseData {
    dad: u8,
    sad: u8,
    lenr: u16,
    response: String,
    responseCode: i8,
}

pub fn data(
    ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> i8 {

    let safe_dad: &mut u8 = unsafe { &mut *dad };
    debug!("dad: {}", safe_dad);

    let safe_sad: &mut u8 = unsafe { &mut *sad };
    debug!("sad: {}", safe_sad);
    debug!("lenc: {}", lenc);

    let safe_command = unsafe { slice::from_raw_parts(command, lenc as usize) };
    debug!("command: {:?}", safe_command);

    let safe_lenr: &mut u16 = unsafe { &mut *lenr };
    debug!("lenr: {}", safe_lenr);

    let safe_response = unsafe { slice::from_raw_parts_mut(response, *safe_lenr as usize) };
    debug!("response with {} slices formed", safe_response.len());

    if !MAP.lock().unwrap().contains_key(&ctn) {
        error!(
            "Card terminal has not been opened. Returning {}",
            ERR_INVALID
        );
        return ERR_INVALID;
    }

    let request_data = RequestData {
        dad: *safe_dad,
        sad: *safe_sad,
        lenc,
        command: encode(safe_command),
        lenr: *safe_lenr,
    };

    let path = get_request_path(ctn);
    let request_body = serde_json::to_string(&request_data).unwrap();
    let response = http::request().post(&path, Some(request_body)).response();

    if response.status() != 200 {
        error!("Request failed! Returning {}", ERR_HTSI);
        return ERR_HTSI;

    }

    let data: ResponseData = match serde_json::from_str(&response.body()) {
        Ok(response) => response,
        Err(error) => {
            error!("Failed to parse response data. {}", error);
            error!("Returning {}", ERR_HTSI);
            return ERR_HTSI;
        }
    };

    if data.responseCode == OK {
        *safe_dad = data.dad;
        *safe_sad = data.sad;
        *safe_lenr = data.lenr;

        let decoded = decode(&data.response).unwrap();
        debug!("Decoded response field {:?}", decoded);

        for (place, element) in safe_response.iter_mut().zip(decoded.iter()) {
            *place = *element;
        }
    }
    debug!("Returning {}", data.responseCode);
    return data.responseCode;
}

fn get_request_path(ctn: u16) -> String {
    let pn = MAP.lock().unwrap();
    let pn = pn.get(&ctn).unwrap();

    format!("ct_data/{}/{}", ctn, pn)
}
<commit_msg>remove unused use statements<commit_after>extern crate serde_json;

use self::super::{ERR_HTSI, ERR_INVALID, MAP, OK};
use self::super::super::http;

use base64::{encode, decode};
use std::slice;

#[derive(Serialize)]
struct RequestData {
    dad: u8,
    sad: u8,
    lenc: u16,
    command: String,
    lenr: u16,
}

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct ResponseData {
    dad: u8,
    sad: u8,
    lenr: u16,
    response: String,
    responseCode: i8,
}

pub fn data(
    ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> i8 {

    let safe_dad: &mut u8 = unsafe { &mut *dad };
    debug!("dad: {}", safe_dad);

    let safe_sad: &mut u8 = unsafe { &mut *sad };
    debug!("sad: {}", safe_sad);
    debug!("lenc: {}", lenc);

    let safe_command = unsafe { slice::from_raw_parts(command, lenc as usize) };
    debug!("command: {:?}", safe_command);

    let safe_lenr: &mut u16 = unsafe { &mut *lenr };
    debug!("lenr: {}", safe_lenr);

    let safe_response = unsafe { slice::from_raw_parts_mut(response, *safe_lenr as usize) };
    debug!("response with {} slices formed", safe_response.len());

    if !MAP.lock().unwrap().contains_key(&ctn) {
        error!(
            "Card terminal has not been opened. Returning {}",
            ERR_INVALID
        );
        return ERR_INVALID;
    }

    let request_data = RequestData {
        dad: *safe_dad,
        sad: *safe_sad,
        lenc,
        command: encode(safe_command),
        lenr: *safe_lenr,
    };

    let path = get_request_path(ctn);
    let request_body = serde_json::to_string(&request_data).unwrap();
    let response = http::request().post(&path, Some(request_body)).response();

    if response.status() != 200 {
        error!("Request failed! Returning {}", ERR_HTSI);
        return ERR_HTSI;

    }

    let data: ResponseData = match serde_json::from_str(&response.body()) {
        Ok(response) => response,
        Err(error) => {
            error!("Failed to parse response data. {}", error);
            error!("Returning {}", ERR_HTSI);
            return ERR_HTSI;
        }
    };

    if data.responseCode == OK {
        *safe_dad = data.dad;
        *safe_sad = data.sad;
        *safe_lenr = data.lenr;

        let decoded = decode(&data.response).unwrap();
        debug!("Decoded response field {:?}", decoded);

        for (place, element) in safe_response.iter_mut().zip(decoded.iter()) {
            *place = *element;
        }
    }
    debug!("Returning {}", data.responseCode);
    return data.responseCode;
}

fn get_request_path(ctn: u16) -> String {
    let pn = MAP.lock().unwrap();
    let pn = pn.get(&ctn).unwrap();

    format!("ct_data/{}/{}", ctn, pn)
}
<|endoftext|>


<commit_before>use platform;
use sized_enum::SizedEnum;
use enum_primitive::FromPrimitive;
use game_map::{RoomId};
use game_state::{GameState, GameMode};

impl GameState {
  fn print_help(&self) {
    println!(
      "Debug mode commands:\n\n\

       quit            - exit debug mode\n\
       goto <room id>  - teleport to a room\n\
       rooms           - list rooms + their contents\n"
    );
  }

  fn goto_room(&mut self, args: Vec<&str>) {
    if let Ok(number) = args[0].parse::<usize>() {
      if let Some(room_id) = RoomId::from_usize(number) {
        self.curr_room = room_id;
        println!("Teleported to {:?}.", room_id);
        return;
      }
    }
    println!("Unknown room id.");
  }

  fn list_rooms(&self) {
    for i in 0..RoomId::size() {
      RoomId::from_usize(i).map(|room_id| {
        let room = self.map.room(room_id);
        println!("Room {} - {:?}", i, room_id);
        if let Some(contents) = room.contents {
          println!("  Contains {:?}", contents);
        }
      });
    }
  }

  pub fn tick_debug_mode(&mut self) {
    platform::show_prompt("debug> ");

    platform::read_input().map(|input| {
      if input == "q" || input == "quit" {
        self.set_mode(GameMode::Primary);
      } else if input == "h" || input == "?" || input == "help" {
        self.print_help();
      } else if input == "rooms" {
        self.list_rooms();
      } else if input.starts_with("goto ") {
        self.goto_room(input.split_whitespace().skip(1).collect());
      } else if input.len() > 0 {
        println!("Unrecognized command. Type ? for help.");
      }
    });
  }
}
<commit_msg>Use new read_input() in debug_mode.rs.<commit_after>use platform;
use sized_enum::SizedEnum;
use enum_primitive::FromPrimitive;
use game_map::{RoomId};
use game_state::{GameState, GameMode};

impl GameState {
  fn print_help(&self) {
    println!(
      "Debug mode commands:\n\n\

       quit            - exit debug mode\n\
       goto <room id>  - teleport to a room\n\
       rooms           - list rooms + their contents\n"
    );
  }

  fn goto_room(&mut self, args: Vec<&str>) {
    if let Ok(number) = args[0].parse::<usize>() {
      if let Some(room_id) = RoomId::from_usize(number) {
        self.curr_room = room_id;
        println!("Teleported to {:?}.", room_id);
        return;
      }
    }
    println!("Unknown room id.");
  }

  fn list_rooms(&self) {
    for i in 0..RoomId::size() {
      RoomId::from_usize(i).map(|room_id| {
        let room = self.map.room(room_id);
        println!("Room {} - {:?}", i, room_id);
        if let Some(contents) = room.contents {
          println!("  Contains {:?}", contents);
        }
      });
    }
  }

  pub fn tick_debug_mode(&mut self) {
    platform::show_prompt("debug> ");

    self.read_input(|state, input| {
      if input == "q" || input == "quit" {
        state.set_mode(GameMode::Primary);
      } else if input == "h" || input == "?" || input == "help" {
        state.print_help();
      } else if input == "rooms" {
        state.list_rooms();
      } else if input.starts_with("goto ") {
        state.goto_room(input.split_whitespace().skip(1).collect());
      } else if input.len() > 0 {
        println!("Unrecognized command. Type ? for help.");
      }
    });
  }
}
<|endoftext|>


<commit_before>use std::{fs, path, io};
#[cfg(target_pointer_width = "32")]
use std::io::{Seek, SeekFrom, Read, Write};

use memmap::{Mmap, Protection};

use CONFIG;
#[cfg(target_pointer_width = "32")]
use super::MAX_CHAINED_OPS;
use util::MHashMap;

/// Holds a file and mmap cache. Because 32 bit systems
/// can't mmap large files, we load them as needed.
pub struct FileCache {
    #[cfg(target_pointer_width = "32")]
    files: MHashMap<path::PathBuf, fs::File>,
    #[cfg(target_pointer_width = "32")]
    fallback: Mmap,
    #[cfg(target_pointer_width = "64")]
    files: MHashMap<path::PathBuf, (fs::File, Mmap)>,
}

impl FileCache {
    pub fn new() -> FileCache {
        FileCache {
            files: MHashMap::default(),
            #[cfg(target_pointer_width = "32")]
            fallback: Mmap::anonymous(MAX_CHAINED_OPS * 16_384, Protection::ReadWrite)
                .expect("mmap failed!"),
        }
    }

    pub fn get_file<R, F: FnMut(&mut fs::File) -> io::Result<R>>(
        &mut self,
        path: &path::Path,
        mut f: F,
    ) -> io::Result<R> {
        self.ensure_exists(path)?;

        #[cfg(target_pointer_width = "32")]
        {
            f(self.files.get_mut(path).unwrap())
        }

        #[cfg(target_pointer_width = "64")]
        {
            f(&mut self.files.get_mut(path).unwrap().0)
        }
    }

    pub fn get_file_range<R, F: FnMut(&mut [u8]) -> R>(
        &mut self,
        path: &path::Path,
        offset: u64,
        len: usize,
        read: bool,
        mut f: F,
    ) -> io::Result<R> {
        self.ensure_exists(path)?;

        #[cfg(target_pointer_width = "32")]
        {
            let file = self.files.get_mut(path).unwrap();
            // TODO: Consider more portable solution based on setting _FILE_OFFSET_BITS=64 or
            // mmap64 rather than this.
            if offset < ::std::usize::MAX as u64 {
                let mut mmap =
                    Mmap::open_with_offset(&file, Protection::ReadWrite, offset as usize, len)?;
                Ok(f(unsafe { mmap.as_mut_slice() }))
            } else {
                file.seek(SeekFrom::Start(offset))?;
                let data = unsafe { &mut self.fallback.as_mut_slice()[0..len] };
                if read {
                    file.read_exact(data)?;
                }
                let res = Ok(f(data));
                if !read {
                    file.write_all(&data)?;
                }
                res
            }
        }

        #[cfg(target_pointer_width = "64")]
        {
            Ok(f(unsafe {
                &mut self.files.get_mut(path).unwrap().1.as_mut_slice()[offset as usize..
                                                                            offset as usize + len]
            }))
        }
    }

    pub fn remove_file(&mut self, path: &path::Path) {
        self.files.remove(path);
    }

    fn ensure_exists(&mut self, path: &path::Path) -> io::Result<()> {
        if !self.files.contains_key(path) {
            if self.files.len() >= CONFIG.net.max_open_files {
                let removal = self.files.iter().map(|(id, _)| id.clone()).next().unwrap();
                self.files.remove(&removal);
            }
            fs::create_dir_all(path.parent().unwrap())?;
            let file = fs::OpenOptions::new()
                .write(true)
                .create(true)
                .read(true)
                .open(path)?;

            #[cfg(target_pointer_width = "32")] self.files.insert(path.to_path_buf(), file);

            #[cfg(target_pointer_width = "64")]
            {
                let mmap = Mmap::open(&file, Protection::ReadWrite)?;
                self.files.insert(path.to_path_buf(), (file, mmap));
            }
        }
        Ok(())
    }
}
<commit_msg>Ensure mapped files are properly flushed<commit_after>use std::{fs, path, io};
#[cfg(target_pointer_width = "32")]
use std::io::{Seek, SeekFrom, Read, Write};

use memmap::{Mmap, Protection};

use CONFIG;
#[cfg(target_pointer_width = "32")]
use super::MAX_CHAINED_OPS;
use util::MHashMap;

/// Holds a file and mmap cache. Because 32 bit systems
/// can't mmap large files, we load them as needed.
pub struct FileCache {
    #[cfg(target_pointer_width = "32")]
    files: MHashMap<path::PathBuf, fs::File>,
    #[cfg(target_pointer_width = "32")]
    fallback: Mmap,
    #[cfg(target_pointer_width = "64")]
    files: MHashMap<path::PathBuf, (fs::File, Mmap)>,
}

impl FileCache {
    pub fn new() -> FileCache {
        FileCache {
            files: MHashMap::default(),
            #[cfg(target_pointer_width = "32")]
            fallback: Mmap::anonymous(MAX_CHAINED_OPS * 16_384, Protection::ReadWrite)
                .expect("mmap failed!"),
        }
    }

    pub fn get_file<R, F: FnMut(&mut fs::File) -> io::Result<R>>(
        &mut self,
        path: &path::Path,
        mut f: F,
    ) -> io::Result<R> {
        self.ensure_exists(path)?;

        #[cfg(target_pointer_width = "32")]
        {
            f(self.files.get_mut(path).unwrap())
        }

        #[cfg(target_pointer_width = "64")]
        {
            f(&mut self.files.get_mut(path).unwrap().0)
        }
    }

    pub fn get_file_range<R, F: FnMut(&mut [u8]) -> R>(
        &mut self,
        path: &path::Path,
        offset: u64,
        len: usize,
        read: bool,
        mut f: F,
    ) -> io::Result<R> {
        self.ensure_exists(path)?;

        #[cfg(target_pointer_width = "32")]
        {
            let file = self.files.get_mut(path).unwrap();
            // TODO: Consider more portable solution based on setting _FILE_OFFSET_BITS=64 or
            // mmap64 rather than this.
            if offset < ::std::usize::MAX as u64 {
                let mut mmap =
                    Mmap::open_with_offset(&file, Protection::ReadWrite, offset as usize, len)?;
                Ok(f(unsafe { mmap.as_mut_slice() }))
            } else {
                file.seek(SeekFrom::Start(offset))?;
                let data = unsafe { &mut self.fallback.as_mut_slice()[0..len] };
                if read {
                    file.read_exact(data)?;
                }
                let res = Ok(f(data));
                if !read {
                    file.write_all(&data)?;
                }
                res
            }
        }

        #[cfg(target_pointer_width = "64")]
        {
            Ok(f(unsafe {
                &mut self.files.get_mut(path).unwrap().1.as_mut_slice()[offset as usize..
                                                                            offset as usize + len]
            }))
        }
    }

    pub fn remove_file(&mut self, path: &path::Path) {
        #[cfg(target_pointer_width = "32")] self.files.remove(path);
        #[cfg(target_pointer_width = "64")] self.files.remove(path).map(|f| f.1.flush_async().ok());
    }

    fn ensure_exists(&mut self, path: &path::Path) -> io::Result<()> {
        if !self.files.contains_key(path) {
            if self.files.len() >= CONFIG.net.max_open_files {
                let removal = self.files.iter().map(|(id, _)| id.clone()).next().unwrap();
                self.remove_file(&removal);
            }
            fs::create_dir_all(path.parent().unwrap())?;
            let file = fs::OpenOptions::new()
                .write(true)
                .create(true)
                .read(true)
                .open(path)?;

            #[cfg(target_pointer_width = "32")] self.files.insert(path.to_path_buf(), file);

            #[cfg(target_pointer_width = "64")]
            {
                let mmap = Mmap::open(&file, Protection::ReadWrite)?;
                self.files.insert(path.to_path_buf(), (file, mmap));
            }
        }
        Ok(())
    }
}

impl Drop for FileCache {
    fn drop(&mut self) {
        #[cfg(target_pointer_width = "64")]
        self.files
            .drain()
            .map(|(_, f)| f.1.flush_async().ok())
            .last();
    }
}
<|endoftext|>