<commit_before>#![cfg(not(windows))]

extern crate libc;
extern crate signal_hook;

use screen::Key;
use screen::Key::*;
use std::io::{Read, Write};
use std::fs::{File, OpenOptions};
use std::os::unix::io::*;
use std::path::*;
use std::process::Command;
use std::process::Stdio;
use std::str;
use std::iter::repeat;
use std::cmp::min;
use ansi;
use ::NEWLINE;

use std::mem;
use std::thread;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::{mpsc, Arc, Mutex};

use self::libc::{dup, SIGINT, SIGWINCH, c_int, c_ushort, c_ulong};
use screen::Screen;

pub struct UnixScreen {
    tty: Terminal,
    start_line: u16,
    desired_rows: u16,
}

impl Screen for UnixScreen {
    fn visible_choices(&self) -> u16 {
        let (_, rows) = self.tty.winsize().unwrap();
        min(self.desired_rows, rows - 1)
    }

    fn width(&self) -> u16 {
        let (cols, _) = self.tty.winsize().unwrap();
        cols
    }

    fn move_cursor_to_prompt_line(&mut self, col: u16) {
        self.reset_cursor();
        self.tty.write(&ansi::cursor_right(col));
    }

    fn blank_screen(&mut self) {
        self.reset_cursor();
        let blank_line = repeat(' ').take(self.width() as usize).collect::<String>();
        for _ in 0..self.visible_choices() + 1 {
            self.tty.write(blank_line.as_bytes());
        }
        self.reset_cursor();
    }

    fn show_cursor(&mut self) {
        self.tty.write(&ansi::show_cursor());
        self.tty.flush();
    }

    fn hide_cursor(&mut self) {
        self.tty.write(&ansi::hide_cursor());
    }

    fn write(&mut self, s: &str) {
        self.tty.write(s.as_bytes());
    }

    fn write_red_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    fn write_red(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    fn write_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    // Return all buffered keystrokes, or the next key if buffer is empty.
    fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(bytes) = self.tty.input.try_recv() {
            ret.extend(bytes);
        }
        while ret.is_empty() {
            let bytes = self.tty.input.recv().unwrap();
            if bytes == vec![SIGWINCH as u8] {
                self.blank_entire_screen();
                return vec![Nothing];
            } else if bytes == vec![SIGINT as u8] {
                return vec![Control('g')];
            } else {
                ret.extend(bytes);
            }
        }
        Terminal::translate_bytes(ret.clone())
    }
}

impl UnixScreen {
    pub fn is_cygwin() -> bool {
        false
    }

    pub fn open_screen(desired_rows: u16) -> UnixScreen {
        let mut tty = Terminal::open_terminal();
        let (_, rows) = tty.winsize().unwrap();
        let visible_choices = min(desired_rows, rows - 1);
        let start_line = rows - visible_choices - 1;
        for _ in 0..visible_choices {
            tty.write(NEWLINE.as_bytes());
        }
        tty.write(&ansi::save_cursor());

        UnixScreen {
            tty: tty,
            start_line: start_line,
            desired_rows: desired_rows,
        }
    }

    fn restore_tty(&mut self) {
        self.tty.restore_tty();
    }

    fn reset_cursor(&mut self) {
        self.tty.write(&ansi::restore_cursor());

        // Writing this carriage return works around a rendering bug in
        // Neovim's terminal emulation. Without it, the cursor flies
        // around all over the place while typing, and the input prompt
        // is not rendered correctly. I have no idea why this workaround
        // is effective, or why others are not, or what the root cause
        // of the issue is (likely something involving save/restore
        // support).
        self.tty.write(b"\r");

        let num_lines = self.visible_choices();
        self.tty.write(&ansi::cursor_up(num_lines));
    }

    pub fn blank_entire_screen(&mut self){
        self.tty.write(&ansi::blank_screen());
    }

    fn more_bytes_needed(bytes: &[u8]) -> bool {
        if let Err(_) = str::from_utf8(bytes) { true } else { false }
    }
}

struct Terminal {
    input: Receiver<Vec<u8>>,
    input_fd: RawFd,
    output: File,
    output_buf: Vec<u8>,
    original_stty_state: Vec<u8>,
}

static mut GLOBAL_TX: *const Arc<Mutex<Sender<Vec<u8>>>> = 0 as *const Arc<Mutex<Sender<Vec<u8>>>>;

fn set_global_tx(tx: Sender<Vec<u8>>) {
    unsafe {
        let singleton = Arc::new(Mutex::new(tx));
        GLOBAL_TX = mem::transmute(Box::new(singleton));
    }
}

fn get_global_tx() -> Sender<Vec<u8>> {
    let singleton = unsafe { (*GLOBAL_TX).clone() };
    let tx = singleton.lock().unwrap();
    tx.clone()
}

fn start_sigwinch_handler() {
    let signals = signal_hook::iterator::Signals::new(&[SIGWINCH, SIGINT]).unwrap();
    thread::spawn(move || {
        for signal in signals.forever() {
            get_global_tx().send(vec![signal as u8]).unwrap();
        }
    });
}

impl Terminal {
    fn open_terminal() -> Terminal {
        let term_path = Path::new("/dev/tty");
        let mut input_file = File::open(&term_path).unwrap();
        let output_file = OpenOptions::new().write(true).open(&term_path).unwrap();
        let input_fd = input_file.as_raw_fd();
        let (tx, rx) = mpsc::channel();
        set_global_tx(tx);

        start_sigwinch_handler();

        let mut ret = Terminal {
            input: rx,
            input_fd: input_fd,
            output: output_file,
            output_buf: Vec::new(),
            original_stty_state: Vec::new(),
        };
        let current_stty_state = ret.stty(&["-g"]);
        ret.original_stty_state = current_stty_state;
        ret.initialize();

        thread::spawn(move || {
            loop {
                let mut buf = [0; 255];
                let tx = get_global_tx();
                if let Ok(length) = input_file.read(&mut buf) {
                    tx.send(buf[0..length].to_vec()).unwrap();
                } else {
                    tx.send([0].to_vec()).unwrap();
                }
            }
        });
        ret
    }

    fn initialize(&mut self) {
        self.stty(&["raw", "-echo", "cbreak", "opost", "onlcr"]);
    }

    fn restore_tty(&mut self) {
        let state = String::from_utf8(self.original_stty_state.clone()).unwrap();
        self.stty(&[&state]);
    }

    fn stty(&mut self, args: &[&str]) -> Vec<u8> {
        let tty_input = unsafe { Stdio::from_raw_fd(dup(self.input_fd)) };
        let mut process = match Command::new("stty")
            .args(args)
            .stdin(tty_input)
            .stdout(Stdio::piped())
            .spawn()
        {
            Ok(p) => p,
            Err(e) => panic!("Spawn failed: {}", e),
        };

        let exit = process.wait();

        let mut buf = Vec::new();
        if exit.unwrap().success() {
            process.stdout.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            let mut str = String::from_utf8(buf).unwrap();

            // The output from `stty -g` may include a newline, which we have to strip off. Otherwise,
            // when we go to restore the tty, stty (on some platforms) will fail with an "invalid
            // argument" error.
            ::trim(&mut str);

            str.into_bytes()
        } else {
            process.stderr.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            panic!(String::from_utf8(buf).unwrap());
        }
    }

    fn translate_bytes(bytes: Vec<u8>) -> Vec<Key> {
        const BEGIN_PASTE: &'static [u8] = b"\x1B[200~";
        const END_PASTE: &'static [u8] = b"\x1B[201~";

        if bytes == b"\x1B[A" || bytes == b"\x1BOA" { return vec![Up] };
        if bytes == b"\x1B[B" || bytes == b"\x1BOB" { return vec![Down] };
        if bytes == b"\x1B[5~" { return vec![PgUp] }
        if bytes == b"\x1B[6~" { return vec![PgDown] }
        if bytes == b"\x1B[4~" { return vec![Home] }
        if bytes == b"\x1B[1~" { return vec![End] }
        if bytes == b"\x1B[Z" { return vec![ShiftTab] };

        let bs = if bytes.starts_with(BEGIN_PASTE) && bytes.ends_with(END_PASTE) {
            let start = BEGIN_PASTE.len();
            let end = bytes.len() - END_PASTE.len();
            bytes[start..end].to_vec()
        } else {
            bytes
        };

        String::from_utf8(bs)
            .unwrap()
            .chars()
            .map(Terminal::translate_char)
            .collect()
    }

    fn translate_char(c: char) -> Key {
        let numeric_char = c as u32;
        if c == '\r' {
            Enter
        } else if numeric_char == 9 {
            Tab
        } else if numeric_char == 127 {
            Backspace
        } else if numeric_char == 27 {
            Control('g')
        } else if numeric_char & 96 == 0 && numeric_char <= 128u32 {
            let c = c as u8;
            Control((c + 96u8) as char)
        } else {
            Char(c)
        }
    }

    fn write(&mut self, s: &[u8]) {
        self.output_buf.extend_from_slice(s);
    }

    fn writeln(&mut self, s: &str) {
        self.output_buf.extend_from_slice(s.as_bytes());
        self.output_buf.push(b'\n');
    }

    fn flush(&mut self) {
        self.output.write(&self.output_buf).unwrap();
        self.output_buf.clear();
    }

    fn winsize(&self) -> Option<(u16, u16)> {
        extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
        #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            Some((size.cols as u16, size.rows as u16))
        } else {
            None
        }
    }
}

impl Drop for Terminal {
    fn drop(&mut self) {
        self.flush();
        self.restore_tty();
    }
}

#[cfg(test)]
mod tests {
    use super::Terminal;

    #[test]
    fn winsize_test() {
        // Travis-CI builds run without a tty, making this test impossible.
        if option_env!("TRAVIS").is_some() {
            // TODO: It should be made obvious from the output that this test was skipped
            return;
        }
        let term = Terminal::open_terminal();
        let (cols, rows) = term.winsize().expect("Failed to get window size!");
        // We don't know the window size a priori, but we can at least
        // assert that it is within some kind of sensible range.
        assert!(cols > 40);
        assert!(rows > 40);
        assert!(cols < 1000);
        assert!(rows < 1000);
    }
}
<commit_msg>Unix: Fix Home/End parsing<commit_after>#![cfg(not(windows))]

extern crate libc;
extern crate signal_hook;

use screen::Key;
use screen::Key::*;
use std::io::{Read, Write};
use std::fs::{File, OpenOptions};
use std::os::unix::io::*;
use std::path::*;
use std::process::Command;
use std::process::Stdio;
use std::str;
use std::iter::repeat;
use std::cmp::min;
use ansi;
use ::NEWLINE;

use std::mem;
use std::thread;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::{mpsc, Arc, Mutex};

use self::libc::{dup, SIGINT, SIGWINCH, c_int, c_ushort, c_ulong};
use screen::Screen;

pub struct UnixScreen {
    tty: Terminal,
    start_line: u16,
    desired_rows: u16,
}

impl Screen for UnixScreen {
    fn visible_choices(&self) -> u16 {
        let (_, rows) = self.tty.winsize().unwrap();
        min(self.desired_rows, rows - 1)
    }

    fn width(&self) -> u16 {
        let (cols, _) = self.tty.winsize().unwrap();
        cols
    }

    fn move_cursor_to_prompt_line(&mut self, col: u16) {
        self.reset_cursor();
        self.tty.write(&ansi::cursor_right(col));
    }

    fn blank_screen(&mut self) {
        self.reset_cursor();
        let blank_line = repeat(' ').take(self.width() as usize).collect::<String>();
        for _ in 0..self.visible_choices() + 1 {
            self.tty.write(blank_line.as_bytes());
        }
        self.reset_cursor();
    }

    fn show_cursor(&mut self) {
        self.tty.write(&ansi::show_cursor());
        self.tty.flush();
    }

    fn hide_cursor(&mut self) {
        self.tty.write(&ansi::hide_cursor());
    }

    fn write(&mut self, s: &str) {
        self.tty.write(s.as_bytes());
    }

    fn write_red_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    fn write_red(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    fn write_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    // Return all buffered keystrokes, or the next key if buffer is empty.
    fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(bytes) = self.tty.input.try_recv() {
            ret.extend(bytes);
        }
        while ret.is_empty() {
            let bytes = self.tty.input.recv().unwrap();
            if bytes == vec![SIGWINCH as u8] {
                self.blank_entire_screen();
                return vec![Nothing];
            } else if bytes == vec![SIGINT as u8] {
                return vec![Control('g')];
            } else {
                ret.extend(bytes);
            }
        }
        Terminal::translate_bytes(ret.clone())
    }
}

impl UnixScreen {
    pub fn is_cygwin() -> bool {
        false
    }

    pub fn open_screen(desired_rows: u16) -> UnixScreen {
        let mut tty = Terminal::open_terminal();
        let (_, rows) = tty.winsize().unwrap();
        let visible_choices = min(desired_rows, rows - 1);
        let start_line = rows - visible_choices - 1;
        for _ in 0..visible_choices {
            tty.write(NEWLINE.as_bytes());
        }
        tty.write(&ansi::save_cursor());

        UnixScreen {
            tty: tty,
            start_line: start_line,
            desired_rows: desired_rows,
        }
    }

    fn restore_tty(&mut self) {
        self.tty.restore_tty();
    }

    fn reset_cursor(&mut self) {
        self.tty.write(&ansi::restore_cursor());

        // Writing this carriage return works around a rendering bug in
        // Neovim's terminal emulation. Without it, the cursor flies
        // around all over the place while typing, and the input prompt
        // is not rendered correctly. I have no idea why this workaround
        // is effective, or why others are not, or what the root cause
        // of the issue is (likely something involving save/restore
        // support).
        self.tty.write(b"\r");

        let num_lines = self.visible_choices();
        self.tty.write(&ansi::cursor_up(num_lines));
    }

    pub fn blank_entire_screen(&mut self){
        self.tty.write(&ansi::blank_screen());
    }

    fn more_bytes_needed(bytes: &[u8]) -> bool {
        if let Err(_) = str::from_utf8(bytes) { true } else { false }
    }
}

struct Terminal {
    input: Receiver<Vec<u8>>,
    input_fd: RawFd,
    output: File,
    output_buf: Vec<u8>,
    original_stty_state: Vec<u8>,
}

static mut GLOBAL_TX: *const Arc<Mutex<Sender<Vec<u8>>>> = 0 as *const Arc<Mutex<Sender<Vec<u8>>>>;

fn set_global_tx(tx: Sender<Vec<u8>>) {
    unsafe {
        let singleton = Arc::new(Mutex::new(tx));
        GLOBAL_TX = mem::transmute(Box::new(singleton));
    }
}

fn get_global_tx() -> Sender<Vec<u8>> {
    let singleton = unsafe { (*GLOBAL_TX).clone() };
    let tx = singleton.lock().unwrap();
    tx.clone()
}

fn start_sigwinch_handler() {
    let signals = signal_hook::iterator::Signals::new(&[SIGWINCH, SIGINT]).unwrap();
    thread::spawn(move || {
        for signal in signals.forever() {
            get_global_tx().send(vec![signal as u8]).unwrap();
        }
    });
}

impl Terminal {
    fn open_terminal() -> Terminal {
        let term_path = Path::new("/dev/tty");
        let mut input_file = File::open(&term_path).unwrap();
        let output_file = OpenOptions::new().write(true).open(&term_path).unwrap();
        let input_fd = input_file.as_raw_fd();
        let (tx, rx) = mpsc::channel();
        set_global_tx(tx);

        start_sigwinch_handler();

        let mut ret = Terminal {
            input: rx,
            input_fd: input_fd,
            output: output_file,
            output_buf: Vec::new(),
            original_stty_state: Vec::new(),
        };
        let current_stty_state = ret.stty(&["-g"]);
        ret.original_stty_state = current_stty_state;
        ret.initialize();

        thread::spawn(move || {
            loop {
                let mut buf = [0; 255];
                let tx = get_global_tx();
                if let Ok(length) = input_file.read(&mut buf) {
                    tx.send(buf[0..length].to_vec()).unwrap();
                } else {
                    tx.send([0].to_vec()).unwrap();
                }
            }
        });
        ret
    }

    fn initialize(&mut self) {
        self.stty(&["raw", "-echo", "cbreak", "opost", "onlcr"]);
    }

    fn restore_tty(&mut self) {
        let state = String::from_utf8(self.original_stty_state.clone()).unwrap();
        self.stty(&[&state]);
    }

    fn stty(&mut self, args: &[&str]) -> Vec<u8> {
        let tty_input = unsafe { Stdio::from_raw_fd(dup(self.input_fd)) };
        let mut process = match Command::new("stty")
            .args(args)
            .stdin(tty_input)
            .stdout(Stdio::piped())
            .spawn()
        {
            Ok(p) => p,
            Err(e) => panic!("Spawn failed: {}", e),
        };

        let exit = process.wait();

        let mut buf = Vec::new();
        if exit.unwrap().success() {
            process.stdout.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            let mut str = String::from_utf8(buf).unwrap();

            // The output from `stty -g` may include a newline, which we have to strip off. Otherwise,
            // when we go to restore the tty, stty (on some platforms) will fail with an "invalid
            // argument" error.
            ::trim(&mut str);

            str.into_bytes()
        } else {
            process.stderr.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            panic!(String::from_utf8(buf).unwrap());
        }
    }

    fn translate_bytes(bytes: Vec<u8>) -> Vec<Key> {
        const BEGIN_PASTE: &'static [u8] = b"\x1B[200~";
        const END_PASTE: &'static [u8] = b"\x1B[201~";

        if bytes == b"\x1B[A" || bytes == b"\x1BOA" { return vec![Up] };
        if bytes == b"\x1B[B" || bytes == b"\x1BOB" { return vec![Down] };
        if bytes == b"\x1B[5~" { return vec![PgUp] }
        if bytes == b"\x1B[6~" { return vec![PgDown] }
        if bytes == b"\x1B[H" { return vec![Home] }
        if bytes == b"\x1B[F" { return vec![End] }
        if bytes == b"\x1B[Z" { return vec![ShiftTab] };

        let bs = if bytes.starts_with(BEGIN_PASTE) && bytes.ends_with(END_PASTE) {
            let start = BEGIN_PASTE.len();
            let end = bytes.len() - END_PASTE.len();
            bytes[start..end].to_vec()
        } else {
            bytes
        };

        String::from_utf8(bs)
            .unwrap()
            .chars()
            .map(Terminal::translate_char)
            .collect()
    }

    fn translate_char(c: char) -> Key {
        let numeric_char = c as u32;
        if c == '\r' {
            Enter
        } else if numeric_char == 9 {
            Tab
        } else if numeric_char == 127 {
            Backspace
        } else if numeric_char == 27 {
            Control('g')
        } else if numeric_char & 96 == 0 && numeric_char <= 128u32 {
            let c = c as u8;
            Control((c + 96u8) as char)
        } else {
            Char(c)
        }
    }

    fn write(&mut self, s: &[u8]) {
        self.output_buf.extend_from_slice(s);
    }

    fn writeln(&mut self, s: &str) {
        self.output_buf.extend_from_slice(s.as_bytes());
        self.output_buf.push(b'\n');
    }

    fn flush(&mut self) {
        self.output.write(&self.output_buf).unwrap();
        self.output_buf.clear();
    }

    fn winsize(&self) -> Option<(u16, u16)> {
        extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
        #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            Some((size.cols as u16, size.rows as u16))
        } else {
            None
        }
    }
}

impl Drop for Terminal {
    fn drop(&mut self) {
        self.flush();
        self.restore_tty();
    }
}

#[cfg(test)]
mod tests {
    use super::Terminal;

    #[test]
    fn winsize_test() {
        // Travis-CI builds run without a tty, making this test impossible.
        if option_env!("TRAVIS").is_some() {
            // TODO: It should be made obvious from the output that this test was skipped
            return;
        }
        let term = Terminal::open_terminal();
        let (cols, rows) = term.winsize().expect("Failed to get window size!");
        // We don't know the window size a priori, but we can at least
        // assert that it is within some kind of sensible range.
        assert!(cols > 40);
        assert!(rows > 40);
        assert!(cols < 1000);
        assert!(rows < 1000);
    }
}
<|endoftext|>


<commit_before>// use std::ptr
//extern crate libc;
use libc::{c_void, size_t};
use libc::funcs::posix88::mman;

pub struct SecureString {
    pub string: Box<String>,
}

impl SecureString {
    pub fn new(string: Box<String>) -> SecureString {
        unsafe { mman::mlock(string.as_ptr() as *const c_void, string.len() as size_t); }
        SecureString { string: string }
    }
}

#[cfg(test)]
mod tests {
    use super::SecureString;

    #[test]
    fn test_new() {
        let str = box "Hello, box!".to_string();
        let sec_str = SecureString::new(str);
        assert_eq!(sec_str.string, box "Hello, box!".to_string())
    }
}
<commit_msg>all the boxes around the string where senseless because String is already on the heap so that ownership moves with SecureString::new(input)<commit_after>// use std::ptr
//extern crate libc;
use libc::{c_void, size_t};
use libc::funcs::posix88::mman;

// Using String to get ownership of the input string
pub struct SecureString {
    pub string: String,
}

impl SecureString {
    pub fn new(string: String) -> SecureString {
        // Lock the string against swapping
        unsafe { mman::mlock(string.as_ptr() as *const c_void, string.len() as size_t); }
        SecureString { string: string }
    }
}

#[cfg(test)]
mod tests {
    use super::SecureString;

    #[test]
    fn test_new() {
        let str = "Hello, box!".to_string();
        // Ownership of str moves to SecureString <- secure input interface
        let sec_str = SecureString::new(str);
        assert_eq!(sec_str.string, "Hello, box!".to_string());
    }
}
<|endoftext|>


<commit_before>import libc::c_double;
import azure::*;
import azure::bindgen::*;
import azure::cairo;
import azure::cairo::bindgen::*;

// A function for spawning into the platform's main thread
fn on_osmain<T: send>(f: fn~(comm::port<T>)) -> comm::chan<T> {
    let builder = task::builder();
    let opts = {
        sched: some({
            mode: task::osmain,
            native_stack_size: none
        })
        with task::get_opts(builder)
    };
    task::set_opts(builder, opts);
    ret task::run_listener(builder, f);
}

// Messages to the platform event handler task
enum osmain_msg {
    get_draw_target(comm::chan<AzDrawTargetRef>),
    add_key_handler(comm::chan<()>),
    draw(comm::chan<()>),
    exit
}

fn main() {
    // The platform event handler thread
    let osmain_ch = on_osmain::<osmain_msg> {|po|
        let mut key_handlers = [];

        sdl::init([
            sdl::init_video
        ]);
        let screen = sdl::video::set_video_mode(
            800, 600, 32,
            [sdl::video::swsurface],
            [sdl::video::doublebuf]);
        assert !screen.is_null();
        let sdl_surf = sdl::video::create_rgb_surface(
            [sdl::video::swsurface],
            800, 600, 32,
            0x00FF0000u32,
            0x0000FF00u32,
            0x000000FFu32,
            0x00000000u32
            );
        assert !sdl_surf.is_null();
        sdl::video::lock_surface(sdl_surf);
        let cairo_surf = unsafe {
            cairo_image_surface_create_for_data(
                unsafe::reinterpret_cast((*sdl_surf).pixels),
                cairo::CAIRO_FORMAT_RGB24,
                (*sdl_surf).w,
                (*sdl_surf).h,
                (*sdl_surf).pitch as libc::c_int
            )
        };
        assert !cairo_surf.is_null();
        let azure_target = AzCreateDrawTargetForCairoSurface(cairo_surf);
        assert !azure_target.is_null();

        loop {
            sdl::event::poll_event {|event|
                alt event {
                  sdl::event::keydown_event(_) {
                    key_handlers.iter {|key_ch|
                        comm::send(key_ch, ())
                    }
                  }
                  _ { }
                }
            }

            // Handle messages
            if comm::peek(po) {
                alt check comm::recv(po) {
                  add_key_handler(key_ch) {
                    key_handlers += [key_ch];
                  }
                  get_draw_target(response_ch) {
                    comm::send(response_ch, azure_target);
                  }
                  draw(response_ch) {
                    sdl::video::unlock_surface(sdl_surf);
                    sdl::video::blit_surface(sdl_surf, ptr::null(),
                                             screen, ptr::null());
                    sdl::video::lock_surface(sdl_surf);
                    sdl::video::flip(screen);
                    comm::send(response_ch, ());
                  }
                  exit { break; }
                }
            }
        }
        AzReleaseDrawTarget(azure_target);
        cairo_surface_destroy(cairo_surf);
        sdl::video::unlock_surface(sdl_surf);
        sdl::quit();
    };

    // The drawing task
    let draw_exit_ch = task::spawn_listener {|exit_po|
        let draw_target_po = comm::port();
        comm::send(osmain_ch, get_draw_target(comm::chan(draw_target_po)));
        let draw_target = comm::recv(draw_target_po);

        let red_color = {
            r: 1f as azure::AzFloat,
            g: 0f as azure::AzFloat,
            b: 0f as azure::AzFloat,
            a: 0.8f as azure::AzFloat
        };
        let red_pattern = AzCreateColorPattern(ptr::addr_of(red_color));

        let green_color = {
            r: 0f as azure::AzFloat,
            g: 1f as azure::AzFloat,
            b: 0f as azure::AzFloat,
            a: 0.8f as azure::AzFloat
        };
        let green_pattern = AzCreateColorPattern(ptr::addr_of(green_color));

        while !comm::peek(exit_po) {
            let red_rect = {
                x: 100f as azure::AzFloat,
                y: 100f as azure::AzFloat,
                width: 200f as azure::AzFloat,
                height: 200f as azure::AzFloat
            };
            AzDrawTargetFillRect(
                draw_target,
                ptr::addr_of(red_rect),
                unsafe { unsafe::reinterpret_cast(red_pattern) }
            );
            let green_rect = {
                x: 200f as azure::AzFloat,
                y: 200f as azure::AzFloat,
                width: 200f as azure::AzFloat,
                height: 200f as azure::AzFloat
            };
            AzDrawTargetFillRect(
                draw_target,
                ptr::addr_of(green_rect),
                unsafe { unsafe::reinterpret_cast(green_pattern) }
            );
            let draw_po = comm::port();
            comm::send(osmain_ch, draw(comm::chan(draw_po)));
            comm::recv(draw_po);
        }

        AzReleaseColorPattern(red_pattern);
        AzReleaseColorPattern(green_pattern);

        let exit_confirm_ch = comm::recv(exit_po);
        comm::send(exit_confirm_ch, ());
    };

    // The keyboard handler
    task::spawn {||
        let key_po = comm::port();
        comm::send(osmain_ch, add_key_handler(comm::chan(key_po)));
        loop {
            alt comm::recv(key_po) {
              _ {
                let draw_exit_confirm_po = comm::port();
                comm::send(draw_exit_ch, comm::chan(draw_exit_confirm_po));
                comm::recv(draw_exit_confirm_po);
                comm::send(osmain_ch, exit);
                break;
              }
            }
        }
    }

}<commit_msg>Only draw on request<commit_after>import libc::c_double;
import azure::*;
import azure::bindgen::*;
import azure::cairo;
import azure::cairo::bindgen::*;

// A function for spawning into the platform's main thread
fn on_osmain<T: send>(f: fn~(comm::port<T>)) -> comm::chan<T> {
    let builder = task::builder();
    let opts = {
        sched: some({
            mode: task::osmain,
            native_stack_size: none
        })
        with task::get_opts(builder)
    };
    task::set_opts(builder, opts);
    ret task::run_listener(builder, f);
}

// Messages to the platform event handler task
enum osmain_msg {
    om_get_draw_target(comm::chan<AzDrawTargetRef>),
    om_add_key_handler(comm::chan<()>),
    om_draw(comm::chan<()>),
    om_exit
}

enum draw_msg {
    dm_draw,
    dm_exit(comm::chan<()>)
}

fn main() {
    // The platform event handler thread
    let osmain_ch = on_osmain::<osmain_msg> {|po|
        let mut key_handlers = [];

        sdl::init([
            sdl::init_video
        ]);
        let screen = sdl::video::set_video_mode(
            800, 600, 32,
            [sdl::video::swsurface],
            [sdl::video::doublebuf]);
        assert !screen.is_null();
        let sdl_surf = sdl::video::create_rgb_surface(
            [sdl::video::swsurface],
            800, 600, 32,
            0x00FF0000u32,
            0x0000FF00u32,
            0x000000FFu32,
            0x00000000u32
            );
        assert !sdl_surf.is_null();
        sdl::video::lock_surface(sdl_surf);
        let cairo_surf = unsafe {
            cairo_image_surface_create_for_data(
                unsafe::reinterpret_cast((*sdl_surf).pixels),
                cairo::CAIRO_FORMAT_RGB24,
                (*sdl_surf).w,
                (*sdl_surf).h,
                (*sdl_surf).pitch as libc::c_int
            )
        };
        assert !cairo_surf.is_null();
        let azure_target = AzCreateDrawTargetForCairoSurface(cairo_surf);
        assert !azure_target.is_null();

        loop {
            sdl::event::poll_event {|event|
                alt event {
                  sdl::event::keydown_event(_) {
                    key_handlers.iter {|key_ch|
                        comm::send(key_ch, ())
                    }
                  }
                  _ { }
                }
            }

            // Handle messages
            if comm::peek(po) {
                alt check comm::recv(po) {
                  om_add_key_handler(key_ch) {
                    key_handlers += [key_ch];
                  }
                  om_get_draw_target(response_ch) {
                    comm::send(response_ch, azure_target);
                  }
                  om_draw(response_ch) {
                    sdl::video::unlock_surface(sdl_surf);
                    sdl::video::blit_surface(sdl_surf, ptr::null(),
                                             screen, ptr::null());
                    sdl::video::lock_surface(sdl_surf);
                    sdl::video::flip(screen);
                    comm::send(response_ch, ());
                  }
                  exit { break; }
                }
            }
        }
        AzReleaseDrawTarget(azure_target);
        cairo_surface_destroy(cairo_surf);
        sdl::video::unlock_surface(sdl_surf);
        sdl::quit();
    };

    // The drawing task
    let draw_ch = task::spawn_listener {|po|
        let draw_target_po = comm::port();
        comm::send(osmain_ch, om_get_draw_target(comm::chan(draw_target_po)));
        let draw_target = comm::recv(draw_target_po);

        let red_color = {
            r: 1f as azure::AzFloat,
            g: 0f as azure::AzFloat,
            b: 0f as azure::AzFloat,
            a: 0.8f as azure::AzFloat
        };
        let red_pattern = AzCreateColorPattern(ptr::addr_of(red_color));

        let green_color = {
            r: 0f as azure::AzFloat,
            g: 1f as azure::AzFloat,
            b: 0f as azure::AzFloat,
            a: 0.8f as azure::AzFloat
        };
        let green_pattern = AzCreateColorPattern(ptr::addr_of(green_color));

        let mut exit_confirm_ch = none;
        loop {
            alt comm::recv::<draw_msg>(po) {
              dm_draw {
                let red_rect = {
                    x: 100f as azure::AzFloat,
                    y: 100f as azure::AzFloat,
                    width: 200f as azure::AzFloat,
                    height: 200f as azure::AzFloat
                };
                AzDrawTargetFillRect(
                    draw_target,
                    ptr::addr_of(red_rect),
                    unsafe { unsafe::reinterpret_cast(red_pattern) }
                );
                let green_rect = {
                    x: 200f as azure::AzFloat,
                    y: 200f as azure::AzFloat,
                    width: 200f as azure::AzFloat,
                    height: 200f as azure::AzFloat
                };
                AzDrawTargetFillRect(
                    draw_target,
                    ptr::addr_of(green_rect),
                    unsafe { unsafe::reinterpret_cast(green_pattern) }
                );
                let draw_po = comm::port();
                comm::send(osmain_ch, om_draw(comm::chan(draw_po)));
                comm::recv(draw_po);
              }
              dm_exit(response_ch) {
                exit_confirm_ch = some(response_ch);
                break;
              }
            }
        }

        AzReleaseColorPattern(red_pattern);
        AzReleaseColorPattern(green_pattern);

        assert exit_confirm_ch.is_some();
        comm::send(exit_confirm_ch.get(), ());
    };

    // The keyboard handler
    task::spawn {||
        let key_po = comm::port();
        comm::send(osmain_ch, om_add_key_handler(comm::chan(key_po)));
        loop {
            alt comm::recv(key_po) {
              _ {
                let draw_exit_confirm_po = comm::port();
                comm::send(draw_ch, dm_exit(comm::chan(draw_exit_confirm_po)));
                comm::recv(draw_exit_confirm_po);
                comm::send(osmain_ch, om_exit);
                break;
              }
            }
        }
    }

    comm::send(draw_ch, dm_draw);
}<|endoftext|>


<commit_before>use consts;
use consts::ErrorCode;
use inproc::InprocCommand;
use msg::Msg;
use options::Options;
use result::{ZmqError, ZmqResult};
use tcp_connecter::TcpConnecter;
use tcp_listener::TcpListener;

use std::collections::HashMap;
use std::comm::Select;
use std::io;
use std::io::Listener;
use std::io::net::ip::SocketAddr;
use std::sync::{Arc, RWLock};

pub enum SocketMessage {
    Ping,
    OnConnected(Sender<Box<Msg>>, Receiver<Box<Msg>>),
}

struct Peer {
    sender: Sender<Box<Msg>>,
    receiver: Receiver<Box<Msg>>,
}

impl Peer {
    fn new(sender: Sender<Box<Msg>>, receiver: Receiver<Box<Msg>>) -> Peer {
        Peer {
            sender: sender,
            receiver: receiver,
        }
    }
}

pub struct SocketBase {
    options: Arc<RWLock<Options>>,
    tx: Sender<ZmqResult<SocketMessage>>,
    rx: Receiver<ZmqResult<SocketMessage>>,
    peers: HashMap<uint, Peer>,
    ids: Vec<uint>,
    inproc_chan: Sender<InprocCommand>,
}

impl SocketBase {
    pub fn new(chan: Sender<InprocCommand>) -> SocketBase {
        let (tx, rx) = channel();
        SocketBase {
            options: Arc::new(RWLock::new(Options::new())),
            tx: tx,
            rx: rx,
            peers: HashMap::new(),
            ids: Vec::new(),
            inproc_chan: chan,
        }
    }

    pub fn set_type(&self, type_: consts::SocketType) {
        self.options.write().type_ = type_ as int;
    }

    pub fn bind(&self, addr: &str) -> ZmqResult<()> {
        let (protocol, address) = try!(parse_uri(addr));

        match protocol {
            "tcp" => {
                match from_str::<SocketAddr>(address) {
                    Some(addr) => {
                        let listener = io::TcpListener::bind(
                            format!("{}:{}", addr.ip, addr.port).as_slice());
                        let acceptor = try!(listener.listen().map_err(ZmqError::from_io_error));
                        TcpListener::spawn_new(acceptor, self.tx.clone(), self.options.clone());
                        Ok(())
                    }
                    None => Err(ZmqError::new(
                        ErrorCode::EINVAL, "Invaid argument: bad address")),
                }
            },
            "inproc" => {
                self.inproc_chan.send(InprocCommand::DoBind(String::from_str(address), self.tx.clone()));
                Ok(())
            },
            _ => Err(ZmqError::new(ErrorCode::EPROTONOSUPPORT, "Protocol not supported")),
        }
    }

    pub fn connect(&self, addr: &str) -> ZmqResult<()> {
        let (protocol, address) = try!(parse_uri(addr));
        match protocol {
            "tcp" => {
                match from_str::<SocketAddr>(address) {
                    Some(addr) => {
                        TcpConnecter::spawn_new(addr, self.tx.clone(), self.options.clone());
                        Ok(())
                    }
                    None => Err(ZmqError::new(
                        ErrorCode::EINVAL, "Invaid argument: bad address")),
                }
            },
            "inproc" => {
                self.inproc_chan.send(InprocCommand::DoConnect(String::from_str(address), self.tx.clone()));
                Ok(())
            },
            _ => Err(ZmqError::new(ErrorCode::EPROTONOSUPPORT, "Protocol not supported")),
        }
    }

    pub fn getsockopt(&self, option: consts::SocketOption) -> int {
        self.options.read().getsockopt(option)
    }

    pub fn recv_first(&mut self) -> (uint, Box<Msg>) {
        loop {
            self.sync_until(|s| { s.peers.len() > 0 });
            let to_remove = {
                let selector = Select::new();
                let mut mapping = HashMap::new();
                let mut index = 0;
                for id in self.ids.iter() {
                    let ref peer = self.peers[*id];
                    let handle = box selector.handle(&peer.receiver);
                    let hid = handle.id();
                    mapping.insert(hid, (Some(handle), index));
                    let handle = mapping.get_mut(&hid).unwrap().mut0().as_mut().unwrap();
                    unsafe {
                        handle.add();
                    }
                    index += 1;
                }
                let handle = box selector.handle(&self.rx);
                let hid = handle.id();
                mapping.insert(hid, (None, 0));
                let hid = selector.wait();
                match mapping.remove(&hid) {
                    Some((None, _)) => continue,
                    Some((Some(mut handle), index)) => {
                        match handle.recv_opt() {
                            Ok(msg) => {
                                unsafe {
                                    handle.remove();
                                }
                                return (self.ids[index], msg);
                            }
                            _ => {
                                unsafe {
                                    handle.remove();
                                }
                                index
                            }
                        }
                    },
                    None => panic!(),
                }
            };
            match self.ids.remove(to_remove) {
                Some(id) => {
                    self.peers.remove(&id);
                },
                None => (),
            }
        }
    }

    pub fn recv_from(&mut self, id: uint) -> Box<Msg> {
        self.sync_until(|s| { s.peers.contains_key(&id) });
        self.peers[id].receiver.recv()
    }

    pub fn send_to(&mut self, id: uint, msg: Box<Msg>) {
        debug!("Sending {} to {}", msg, id);
        self.sync_until(|s| { s.peers.contains_key(&id) });
        self.peers[id].sender.send(msg);
    }

    pub fn round_robin(&mut self, mut index: uint) -> (uint, uint) {
        self.sync_until(|s| { s.ids.len() > 0 });
        index = (index + 1) % self.ids.len();
        (index, self.ids[index])
    }

    fn sync_until(&mut self, cond: |&SocketBase| -> bool) {
        loop {
            if !cond(self) {
                debug!("Condition not met, wait... peers: {}", self.peers.len());
                match self.rx.recv_opt() {
                    Ok(msg) => self.handle_msg(msg),
                    Err(_) => panic!(),
                }
                continue;
            }
            match self.rx.try_recv() {
                Ok(msg) => self.handle_msg(msg),
                Err(_) => break,
            }
        }
    }

    fn handle_msg(&mut self, msg: ZmqResult<SocketMessage>) {
        match msg {
            Ok(SocketMessage::OnConnected(tx, rx)) => {
                let id = *self.ids.last().unwrap_or(&0) + 1;
                debug!("New peer: {}", id);
                self.ids.push(id);
                self.peers.insert(id, Peer::new(tx, rx));
            }
            _ => (),
        }
    }
}

fn parse_uri<'r>(uri: &'r str) -> ZmqResult<(&'r str, &'r str)> {
    match uri.find_str("://") {
        Some(pos) => {
            let protocol = uri.slice_to(pos);
            let address = uri.slice_from(pos + 3);
            if protocol.len() == 0 || address.len() == 0 {
                Err(ZmqError::new(
                    ErrorCode::EINVAL,
                    "Invalid argument: missing protocol or address"))
            } else {
                Ok((protocol, address))
            }
        },
        None => Err(ZmqError::new(
            ErrorCode::EINVAL, "Invalid argument: missing ://")),
    }
}

#[cfg(test)]
mod test {
    use super::parse_uri;

    #[test]
    fn test_parse_uri() {
        assert!(parse_uri("").is_err());
        assert!(parse_uri("://").is_err());
        assert!(parse_uri("tcp://").is_err());
        assert!(parse_uri("://127.0.0.1").is_err());
        match parse_uri("tcp://127.0.0.1:8890") {
            Ok((protocol, address)) => {
                assert_eq!(protocol, "tcp");
                assert_eq!(address, "127.0.0.1:8890");
            },
            Err(_) => {assert!(false);},
        }
    }
}
<commit_msg>Use tuple indexing<commit_after>use consts;
use consts::ErrorCode;
use inproc::InprocCommand;
use msg::Msg;
use options::Options;
use result::{ZmqError, ZmqResult};
use tcp_connecter::TcpConnecter;
use tcp_listener::TcpListener;

use std::collections::HashMap;
use std::comm::Select;
use std::io;
use std::io::Listener;
use std::io::net::ip::SocketAddr;
use std::sync::{Arc, RWLock};

pub enum SocketMessage {
    Ping,
    OnConnected(Sender<Box<Msg>>, Receiver<Box<Msg>>),
}

struct Peer {
    sender: Sender<Box<Msg>>,
    receiver: Receiver<Box<Msg>>,
}

impl Peer {
    fn new(sender: Sender<Box<Msg>>, receiver: Receiver<Box<Msg>>) -> Peer {
        Peer {
            sender: sender,
            receiver: receiver,
        }
    }
}

pub struct SocketBase {
    options: Arc<RWLock<Options>>,
    tx: Sender<ZmqResult<SocketMessage>>,
    rx: Receiver<ZmqResult<SocketMessage>>,
    peers: HashMap<uint, Peer>,
    ids: Vec<uint>,
    inproc_chan: Sender<InprocCommand>,
}

impl SocketBase {
    pub fn new(chan: Sender<InprocCommand>) -> SocketBase {
        let (tx, rx) = channel();
        SocketBase {
            options: Arc::new(RWLock::new(Options::new())),
            tx: tx,
            rx: rx,
            peers: HashMap::new(),
            ids: Vec::new(),
            inproc_chan: chan,
        }
    }

    pub fn set_type(&self, type_: consts::SocketType) {
        self.options.write().type_ = type_ as int;
    }

    pub fn bind(&self, addr: &str) -> ZmqResult<()> {
        let (protocol, address) = try!(parse_uri(addr));

        match protocol {
            "tcp" => {
                match from_str::<SocketAddr>(address) {
                    Some(addr) => {
                        let listener = io::TcpListener::bind(
                            format!("{}:{}", addr.ip, addr.port).as_slice());
                        let acceptor = try!(listener.listen().map_err(ZmqError::from_io_error));
                        TcpListener::spawn_new(acceptor, self.tx.clone(), self.options.clone());
                        Ok(())
                    }
                    None => Err(ZmqError::new(
                        ErrorCode::EINVAL, "Invaid argument: bad address")),
                }
            },
            "inproc" => {
                self.inproc_chan.send(InprocCommand::DoBind(String::from_str(address), self.tx.clone()));
                Ok(())
            },
            _ => Err(ZmqError::new(ErrorCode::EPROTONOSUPPORT, "Protocol not supported")),
        }
    }

    pub fn connect(&self, addr: &str) -> ZmqResult<()> {
        let (protocol, address) = try!(parse_uri(addr));
        match protocol {
            "tcp" => {
                match from_str::<SocketAddr>(address) {
                    Some(addr) => {
                        TcpConnecter::spawn_new(addr, self.tx.clone(), self.options.clone());
                        Ok(())
                    }
                    None => Err(ZmqError::new(
                        ErrorCode::EINVAL, "Invaid argument: bad address")),
                }
            },
            "inproc" => {
                self.inproc_chan.send(InprocCommand::DoConnect(String::from_str(address), self.tx.clone()));
                Ok(())
            },
            _ => Err(ZmqError::new(ErrorCode::EPROTONOSUPPORT, "Protocol not supported")),
        }
    }

    pub fn getsockopt(&self, option: consts::SocketOption) -> int {
        self.options.read().getsockopt(option)
    }

    pub fn recv_first(&mut self) -> (uint, Box<Msg>) {
        loop {
            self.sync_until(|s| { s.peers.len() > 0 });
            let to_remove = {
                let selector = Select::new();
                let mut mapping = HashMap::new();
                let mut index = 0;
                for id in self.ids.iter() {
                    let ref peer = self.peers[*id];
                    let handle = box selector.handle(&peer.receiver);
                    let hid = handle.id();
                    mapping.insert(hid, (Some(handle), index));
                    let handle = mapping.get_mut(&hid).unwrap().0.as_mut().unwrap();
                    unsafe {
                        handle.add();
                    }
                    index += 1;
                }
                let handle = box selector.handle(&self.rx);
                let hid = handle.id();
                mapping.insert(hid, (None, 0));
                let hid = selector.wait();
                match mapping.remove(&hid) {
                    Some((None, _)) => continue,
                    Some((Some(mut handle), index)) => {
                        match handle.recv_opt() {
                            Ok(msg) => {
                                unsafe {
                                    handle.remove();
                                }
                                return (self.ids[index], msg);
                            }
                            _ => {
                                unsafe {
                                    handle.remove();
                                }
                                index
                            }
                        }
                    },
                    None => panic!(),
                }
            };
            match self.ids.remove(to_remove) {
                Some(id) => {
                    self.peers.remove(&id);
                },
                None => (),
            }
        }
    }

    pub fn recv_from(&mut self, id: uint) -> Box<Msg> {
        self.sync_until(|s| { s.peers.contains_key(&id) });
        self.peers[id].receiver.recv()
    }

    pub fn send_to(&mut self, id: uint, msg: Box<Msg>) {
        debug!("Sending {} to {}", msg, id);
        self.sync_until(|s| { s.peers.contains_key(&id) });
        self.peers[id].sender.send(msg);
    }

    pub fn round_robin(&mut self, mut index: uint) -> (uint, uint) {
        self.sync_until(|s| { s.ids.len() > 0 });
        index = (index + 1) % self.ids.len();
        (index, self.ids[index])
    }

    fn sync_until(&mut self, cond: |&SocketBase| -> bool) {
        loop {
            if !cond(self) {
                debug!("Condition not met, wait... peers: {}", self.peers.len());
                match self.rx.recv_opt() {
                    Ok(msg) => self.handle_msg(msg),
                    Err(_) => panic!(),
                }
                continue;
            }
            match self.rx.try_recv() {
                Ok(msg) => self.handle_msg(msg),
                Err(_) => break,
            }
        }
    }

    fn handle_msg(&mut self, msg: ZmqResult<SocketMessage>) {
        match msg {
            Ok(SocketMessage::OnConnected(tx, rx)) => {
                let id = *self.ids.last().unwrap_or(&0) + 1;
                debug!("New peer: {}", id);
                self.ids.push(id);
                self.peers.insert(id, Peer::new(tx, rx));
            }
            _ => (),
        }
    }
}

fn parse_uri<'r>(uri: &'r str) -> ZmqResult<(&'r str, &'r str)> {
    match uri.find_str("://") {
        Some(pos) => {
            let protocol = uri.slice_to(pos);
            let address = uri.slice_from(pos + 3);
            if protocol.len() == 0 || address.len() == 0 {
                Err(ZmqError::new(
                    ErrorCode::EINVAL,
                    "Invalid argument: missing protocol or address"))
            } else {
                Ok((protocol, address))
            }
        },
        None => Err(ZmqError::new(
            ErrorCode::EINVAL, "Invalid argument: missing ://")),
    }
}

#[cfg(test)]
mod test {
    use super::parse_uri;

    #[test]
    fn test_parse_uri() {
        assert!(parse_uri("").is_err());
        assert!(parse_uri("://").is_err());
        assert!(parse_uri("tcp://").is_err());
        assert!(parse_uri("://127.0.0.1").is_err());
        match parse_uri("tcp://127.0.0.1:8890") {
            Ok((protocol, address)) => {
                assert_eq!(protocol, "tcp");
                assert_eq!(address, "127.0.0.1:8890");
            },
            Err(_) => {assert!(false);},
        }
    }
}
<|endoftext|>


<commit_before>//! The types in this module are all shorthand for `PredicateType<Lhs,
//! AsExpr<Rhs, Lhs>>`. Since we often need to return concrete types, instead of
//! a boxed trait object, these can be useful for writing concise return types.
use super::array_comparison::{AsInExpression, In, NotIn};
use super::grouped::Grouped;
use super::select_by::SelectBy;
use super::{AsExpression, Expression};
use crate::sql_types;

/// The SQL type of an expression
pub type SqlTypeOf<Expr> = <Expr as Expression>::SqlType;

/// The type of `Item` when converted to an expression with the same type as `TargetExpr`
pub type AsExpr<Item, TargetExpr> = AsExprOf<Item, SqlTypeOf<TargetExpr>>;

/// The type of `Item` when converted to an expression of `Type`
pub type AsExprOf<Item, Type> = <Item as AsExpression<Type>>::Expression;

/// The return type of
/// [`lhs.eq(rhs)`](crate::expression_methods::ExpressionMethods::eq())
pub type Eq<Lhs, Rhs> = Grouped<super::operators::Eq<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.ne(rhs)`](crate::expression_methods::ExpressionMethods::ne())
pub type NotEq<Lhs, Rhs> = Grouped<super::operators::NotEq<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.eq_any(rhs)`](crate::expression_methods::ExpressionMethods::eq_any())
pub type EqAny<Lhs, Rhs> = Grouped<In<Lhs, <Rhs as AsInExpression<SqlTypeOf<Lhs>>>::InExpression>>;

/// The return type of
/// [`lhs.ne_all(rhs)`](crate::expression_methods::ExpressionMethods::ne_all())
pub type NeAny<Lhs, Rhs> =
    Grouped<NotIn<Lhs, <Rhs as AsInExpression<SqlTypeOf<Lhs>>>::InExpression>>;

/// The return type of
/// [`expr.is_null()`](crate::expression_methods::ExpressionMethods::is_null())
pub type IsNull<Expr> = Grouped<super::operators::IsNull<Expr>>;

/// The return type of
/// [`expr.is_not_null()`](crate::expression_methods::ExpressionMethods::is_not_null())
pub type IsNotNull<Expr> = Grouped<super::operators::IsNotNull<Expr>>;

/// The return type of
/// [`lhs.gt(rhs)`](crate::expression_methods::ExpressionMethods::gt())
pub type Gt<Lhs, Rhs> = Grouped<super::operators::Gt<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.ge(rhs)`](crate::expression_methods::ExpressionMethods::ge())
pub type GtEq<Lhs, Rhs> = Grouped<super::operators::GtEq<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.lt(rhs)`](crate::expression_methods::ExpressionMethods::lt())
pub type Lt<Lhs, Rhs> = Grouped<super::operators::Lt<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.le(rhs)`](crate::expression_methods::ExpressionMethods::le())
pub type LtEq<Lhs, Rhs> = Grouped<super::operators::LtEq<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.between(lower, upper)`](crate::expression_methods::ExpressionMethods::between())
pub type Between<Lhs, Lower, Upper> = Grouped<
    super::operators::Between<Lhs, super::operators::And<AsExpr<Lower, Lhs>, AsExpr<Upper, Lhs>>>,
>;

/// The return type of
/// [`lhs.not_between(lower, upper)`](crate::expression_methods::ExpressionMethods::not_between())
pub type NotBetween<Lhs, Lower, Upper> = Grouped<
    super::operators::NotBetween<
        Lhs,
        super::operators::And<AsExpr<Lower, Lhs>, AsExpr<Upper, Lhs>>,
    >,
>;

/// The return type of
/// [`lhs.concat(rhs)`](crate::expression_methods::TextExpressionMethods::concat())
pub type Concat<Lhs, Rhs> = Grouped<super::operators::Concat<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`expr.desc()`](crate::expression_methods::ExpressionMethods::desc())
pub type Desc<Expr> = super::operators::Desc<Expr>;

/// The return type of
/// [`expr.asc()`](crate::expression_methods::ExpressionMethods::asc())
pub type Asc<Expr> = super::operators::Asc<Expr>;

/// The return type of
/// [`expr.nullable()`](crate::expression_methods::NullableExpressionMethods::nullable())
pub type Nullable<Expr> = super::nullable::Nullable<Expr>;

/// The return type of
/// [`expr.assume_not_null()`](crate::expression_methods::ExpressionMethods::assume_not_null())
pub type AssumeNotNull<Expr> = super::assume_not_null::AssumeNotNull<Expr>;

/// The return type of
/// [`lhs.and(rhs)`](crate::expression_methods::BoolExpressionMethods::and())
pub type And<Lhs, Rhs, ST = sql_types::Bool> =
    Grouped<super::operators::And<Lhs, AsExprOf<Rhs, ST>>>;

/// The return type of
/// [`lhs.or(rhs)`](crate::expression_methods::BoolExpressionMethods::or())
pub type Or<Lhs, Rhs, ST = sql_types::Bool> = Grouped<super::operators::Or<Lhs, AsExprOf<Rhs, ST>>>;

/// The return type of
/// [`lhs.escape('x')`](crate::expression_methods::EscapeExpressionMethods::escape())
pub type Escape<Lhs> = Grouped<
    super::operators::Escape<
        <Lhs as crate::expression_methods::EscapeExpressionMethods>::TextExpression,
        AsExprOf<String, sql_types::VarChar>,
    >,
>;

/// The return type of
/// [`lhs.like(rhs)`](crate::expression_methods::TextExpressionMethods::like())
pub type Like<Lhs, Rhs> = Grouped<super::operators::Like<Lhs, AsExprOf<Rhs, SqlTypeOf<Lhs>>>>;

/// The return type of
/// [`lhs.not_like(rhs)`](crate::expression_methods::TextExpressionMethods::not_like())
pub type NotLike<Lhs, Rhs> = Grouped<super::operators::NotLike<Lhs, AsExprOf<Rhs, SqlTypeOf<Lhs>>>>;

/// Represents the return type of [`.as_select()`](crate::prelude::SelectableHelper::as_select)
pub type AsSelect<Source, DB> = SelectBy<Source, DB>;

// we allow unreachable_pub here
// as rustc otherwise shows false positives
// for every item in this module. We reexport
// everything from `crate::helper_types::`
#[doc(inline)]
#[allow(unreachable_pub)]
pub use super::functions::helper_types::*;

#[doc(inline)]
#[cfg(feature = "postgres_backend")]
#[allow(unreachable_pub)]
pub use crate::pg::expression::helper_types::*;

#[doc(inline)]
#[cfg(feature = "sqlite")]
#[allow(unreachable_pub)]
pub use crate::sqlite::expression::helper_types::*;
<commit_msg>Fix broken doc link<commit_after>//! The types in this module are all shorthand for `PredicateType<Lhs,
//! AsExpr<Rhs, Lhs>>`. Since we often need to return concrete types, instead of
//! a boxed trait object, these can be useful for writing concise return types.
use super::array_comparison::{AsInExpression, In, NotIn};
use super::grouped::Grouped;
use super::select_by::SelectBy;
use super::{AsExpression, Expression};
use crate::sql_types;

/// The SQL type of an expression
pub type SqlTypeOf<Expr> = <Expr as Expression>::SqlType;

/// The type of `Item` when converted to an expression with the same type as `TargetExpr`
pub type AsExpr<Item, TargetExpr> = AsExprOf<Item, SqlTypeOf<TargetExpr>>;

/// The type of `Item` when converted to an expression of `Type`
pub type AsExprOf<Item, Type> = <Item as AsExpression<Type>>::Expression;

/// The return type of
/// [`lhs.eq(rhs)`](crate::expression_methods::ExpressionMethods::eq())
pub type Eq<Lhs, Rhs> = Grouped<super::operators::Eq<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.ne(rhs)`](crate::expression_methods::ExpressionMethods::ne())
pub type NotEq<Lhs, Rhs> = Grouped<super::operators::NotEq<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.eq_any(rhs)`](crate::expression_methods::ExpressionMethods::eq_any())
pub type EqAny<Lhs, Rhs> = Grouped<In<Lhs, <Rhs as AsInExpression<SqlTypeOf<Lhs>>>::InExpression>>;

/// The return type of
/// [`lhs.ne_all(rhs)`](crate::expression_methods::ExpressionMethods::ne_all())
pub type NeAny<Lhs, Rhs> =
    Grouped<NotIn<Lhs, <Rhs as AsInExpression<SqlTypeOf<Lhs>>>::InExpression>>;

/// The return type of
/// [`expr.is_null()`](crate::expression_methods::ExpressionMethods::is_null())
pub type IsNull<Expr> = Grouped<super::operators::IsNull<Expr>>;

/// The return type of
/// [`expr.is_not_null()`](crate::expression_methods::ExpressionMethods::is_not_null())
pub type IsNotNull<Expr> = Grouped<super::operators::IsNotNull<Expr>>;

/// The return type of
/// [`lhs.gt(rhs)`](crate::expression_methods::ExpressionMethods::gt())
pub type Gt<Lhs, Rhs> = Grouped<super::operators::Gt<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.ge(rhs)`](crate::expression_methods::ExpressionMethods::ge())
pub type GtEq<Lhs, Rhs> = Grouped<super::operators::GtEq<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.lt(rhs)`](crate::expression_methods::ExpressionMethods::lt())
pub type Lt<Lhs, Rhs> = Grouped<super::operators::Lt<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.le(rhs)`](crate::expression_methods::ExpressionMethods::le())
pub type LtEq<Lhs, Rhs> = Grouped<super::operators::LtEq<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`lhs.between(lower, upper)`](crate::expression_methods::ExpressionMethods::between())
pub type Between<Lhs, Lower, Upper> = Grouped<
    super::operators::Between<Lhs, super::operators::And<AsExpr<Lower, Lhs>, AsExpr<Upper, Lhs>>>,
>;

/// The return type of
/// [`lhs.not_between(lower, upper)`](crate::expression_methods::ExpressionMethods::not_between())
pub type NotBetween<Lhs, Lower, Upper> = Grouped<
    super::operators::NotBetween<
        Lhs,
        super::operators::And<AsExpr<Lower, Lhs>, AsExpr<Upper, Lhs>>,
    >,
>;

/// The return type of
/// [`lhs.concat(rhs)`](crate::expression_methods::TextExpressionMethods::concat())
pub type Concat<Lhs, Rhs> = Grouped<super::operators::Concat<Lhs, AsExpr<Rhs, Lhs>>>;

/// The return type of
/// [`expr.desc()`](crate::expression_methods::ExpressionMethods::desc())
pub type Desc<Expr> = super::operators::Desc<Expr>;

/// The return type of
/// [`expr.asc()`](crate::expression_methods::ExpressionMethods::asc())
pub type Asc<Expr> = super::operators::Asc<Expr>;

/// The return type of
/// [`expr.nullable()`](crate::expression_methods::NullableExpressionMethods::nullable())
pub type Nullable<Expr> = super::nullable::Nullable<Expr>;

/// The return type of
/// [`expr.assume_not_null()`](crate::expression_methods::NullableExpressionMethods::assume_not_null())
pub type AssumeNotNull<Expr> = super::assume_not_null::AssumeNotNull<Expr>;

/// The return type of
/// [`lhs.and(rhs)`](crate::expression_methods::BoolExpressionMethods::and())
pub type And<Lhs, Rhs, ST = sql_types::Bool> =
    Grouped<super::operators::And<Lhs, AsExprOf<Rhs, ST>>>;

/// The return type of
/// [`lhs.or(rhs)`](crate::expression_methods::BoolExpressionMethods::or())
pub type Or<Lhs, Rhs, ST = sql_types::Bool> = Grouped<super::operators::Or<Lhs, AsExprOf<Rhs, ST>>>;

/// The return type of
/// [`lhs.escape('x')`](crate::expression_methods::EscapeExpressionMethods::escape())
pub type Escape<Lhs> = Grouped<
    super::operators::Escape<
        <Lhs as crate::expression_methods::EscapeExpressionMethods>::TextExpression,
        AsExprOf<String, sql_types::VarChar>,
    >,
>;

/// The return type of
/// [`lhs.like(rhs)`](crate::expression_methods::TextExpressionMethods::like())
pub type Like<Lhs, Rhs> = Grouped<super::operators::Like<Lhs, AsExprOf<Rhs, SqlTypeOf<Lhs>>>>;

/// The return type of
/// [`lhs.not_like(rhs)`](crate::expression_methods::TextExpressionMethods::not_like())
pub type NotLike<Lhs, Rhs> = Grouped<super::operators::NotLike<Lhs, AsExprOf<Rhs, SqlTypeOf<Lhs>>>>;

/// Represents the return type of [`.as_select()`](crate::prelude::SelectableHelper::as_select)
pub type AsSelect<Source, DB> = SelectBy<Source, DB>;

// we allow unreachable_pub here
// as rustc otherwise shows false positives
// for every item in this module. We reexport
// everything from `crate::helper_types::`
#[doc(inline)]
#[allow(unreachable_pub)]
pub use super::functions::helper_types::*;

#[doc(inline)]
#[cfg(feature = "postgres_backend")]
#[allow(unreachable_pub)]
pub use crate::pg::expression::helper_types::*;

#[doc(inline)]
#[cfg(feature = "sqlite")]
#[allow(unreachable_pub)]
pub use crate::sqlite::expression::helper_types::*;
<|endoftext|>


<commit_before>/// Strongly borrowed from TypedArena
/// Differs in growth strategy (non-doubling)
/// And only for variable-sized strings

use std::borrow::Borrow;
use std::cell::{Cell,RefCell};
use std::cmp::max;
use std::collections::LinkedList;
use std::collections::hash_map::Entry::{Occupied,Vacant};
use std::collections::hash_map::HashMap;
use std::default::Default;
use std::ops::Deref;
use std::slice;
use std::{fmt,hash,mem,ptr,str};

struct Chunk {
    start: *mut u8,
    capacity: usize,
}

impl Chunk {
    fn new(capacity: usize) -> Chunk {
        let mut slab: Vec<u8> = Vec::with_capacity(capacity);
        let start = slab.as_mut_ptr();

        // We will manually track the buffer and then drop it ourselves
        unsafe {
            mem::forget(slab);
        }

        Chunk {
            start: start,
            capacity: capacity,
        }
    }

    // Returns a pointer to the beginning of the allocated space.
    #[inline]
    fn start(&self) -> *const u8 {
        self.start as *const u8
    }

    // Returns a pointer to the end of the allocated space.
    #[inline]
    fn end(&self) -> *const u8 {
        unsafe { self.start().offset(self.capacity as isize) }
    }
}

impl Drop for Chunk {
    fn drop(&mut self) {
        // This is safe because we only ever store u8, which doesn't
        // have a destructor. This means the len doesn't matter, only
        // the capacity.
        unsafe {
            Vec::from_raw_parts(self.start, self.capacity, self.capacity);
        }
    }
}

#[derive(Copy,Clone)]
pub struct InternedString {
    data: *const u8,
    len: usize,
}

impl InternedString {
    fn from_parts(data: *const u8, len: usize) -> InternedString {
        InternedString {
            data: data,
            len: len,
        }
    }

    pub fn from_str(s: &str) -> InternedString {
        let bytes: &[u8] = s.as_bytes();
        InternedString {
            data: bytes.as_ptr(),
            len: bytes.len(),
        }
    }

    pub fn as_slice<'s>(&self) -> &'s str {
        unsafe {
            let bytes = slice::from_raw_parts(self.data, self.len);
            str::from_utf8_unchecked(bytes)
        }
    }
}

impl fmt::Debug for InternedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl PartialEq for InternedString {
    fn eq(&self, other: &InternedString) -> bool {
        self.as_slice().eq(other.as_slice())
    }
}

impl PartialEq<str> for InternedString {
    fn eq(&self, other: &str) -> bool {
        self.as_slice().eq(other)
    }
}

impl Eq for InternedString {}

impl hash::Hash for InternedString {
    fn hash<H>(&self, state: &mut H)
        where H: hash::Hasher
    {
        self.as_slice().hash(state)
    }
}

impl Borrow<str> for InternedString {
    fn borrow(&self) -> &str {
        self.as_slice()
    }
}

impl Deref for InternedString {
    type Target = str;

    fn deref(&self) -> &str {
        self.as_slice()
    }
}

pub struct StringPool {
    start: Cell<*mut u8>,
    end: Cell<*const u8>,
    chunks: RefCell<LinkedList<Chunk>>,
    index: RefCell<HashMap<InternedString, InternedString>>,
}

static CAPACITY: usize = 10240;

impl StringPool {
    pub fn new() -> StringPool {
        StringPool{
            start: Cell::new(ptr::null_mut()),
            end: Cell::new(ptr::null()),
            chunks: RefCell::new(LinkedList::new()),
            index: RefCell::new(Default::default()),
        }
    }

    pub fn intern<'s>(&'s self, s: &str) -> &'s str {
        if s == "" { return ""; }

        let search_string = InternedString::from_str(s);

        let interned_str = match self.index.borrow_mut().entry(search_string) {
            Occupied(entry) => *entry.get(),
            Vacant(entry) => *entry.insert(self.do_intern(s)),
        };

        // The lifetime is really matched to us
        unsafe { mem::transmute(interned_str) }
    }

    fn do_intern(&self, s: &str) -> InternedString {
        self.ensure_capacity(s.len());
        self.store(s)
    }

    fn ensure_capacity(&self, str_len: usize) {
        let remaining = self.end.get() as usize - self.start.get() as usize;
        if remaining < str_len {
            self.allocate_another(max(CAPACITY, str_len))
        }
    }

    fn allocate_another(&self, capacity: usize) {
        let chunk = Chunk::new(capacity);
        self.start.set(chunk.start() as *mut u8);
        self.end.set(chunk.end());
        self.chunks.borrow_mut().push_front(chunk);
    }

    fn store(&self, s: &str) -> InternedString {
        let str_start = s.as_bytes().as_ptr();
        let str_len = s.len();

        unsafe {
            ptr::copy_nonoverlapping(str_start, self.start.get(), str_len);

            // Rebuild the string from our buffer
            let interned_str = InternedString::from_parts(self.start.get() as *const u8, str_len);

            // Increase current pointer
            self.start.set(self.start.get().offset(str_len as isize));

            interned_str
        }
    }
}

#[cfg(test)]
mod test {
    use std::borrow::ToOwned;

    use super::StringPool;

    #[test]
    fn keeps_the_same_string() {
        let s = StringPool::new();

        let interned = s.intern("hello");

        assert_eq!(interned, "hello");
    }

    #[test]
    fn does_not_reuse_the_pointer_of_the_input() {
        let s = StringPool::new();
        let input = "hello";

        let interned = s.intern(input);

        assert!(
            input.as_bytes().as_ptr() !=
            interned.as_bytes().as_ptr()
        );
    }

    #[test]
    fn reuses_the_pointer_for_repeated_input() {
        let s = StringPool::new();

        let interned1 = s.intern("world");
        let interned2 = s.intern("world");

        assert_eq!(
            interned1.as_bytes().as_ptr(),
            interned2.as_bytes().as_ptr()
        );
    }

    #[test]
    #[cfg(feature = "compile_failure")]
    fn string_cannot_outlive_the_pool() {
        let z = {
            let s = StringPool::new();
            s.intern("hello")
        };
    }

    #[test]
    fn ignores_the_lifetime_of_the_input_string() {
        let s = StringPool::new();

        let interned = {
            let allocated_string = "green".to_owned();
            s.intern(&allocated_string)
        };

        // allocated_string is gone now, but we should be able to
        // access the result value until the storage goes away.

        assert_eq!(interned, "green");
    }

    #[test]
    fn can_be_dropped_immediately() {
        StringPool::new();
    }

    fn return_populated_storage() -> (StringPool, *const u8) {
        let s = StringPool::new();
        let ptr = {
            let interned = s.intern("hello");
            interned.as_ptr()
        };
        (s, ptr)
    }

    #[test]
    fn can_return_storage_populated_with_values() {
        let (s, ptr_val) = return_populated_storage();
        let interned = s.intern("hello");
        assert_eq!(interned.as_ptr(), ptr_val);
    }
}

#[cfg(test)]
mod bench {
    use test::Bencher;

    use super::StringPool;

    #[bench]
    fn single_string(b: &mut Bencher) {
        let s = StringPool::new();
        b.iter(|| s.intern("hello"));
        b.bytes = "hello".len() as u64;
    }

    #[bench]
    fn many_unique_string(b: &mut Bencher) {
        let s = StringPool::new();

        let strings: Vec<String> =
            (0..1000)
            .map(|i| format!("str{}str", i))
            .collect();
        b.iter(|| {
            for ss in strings.iter() {
                s.intern(ss);
            }
        });
        b.bytes = strings.iter().fold(0, |a, s| a + s.len()) as u64;
    }

    #[bench]
    fn many_repeated_string(b: &mut Bencher) {
        let s = StringPool::new();

        let strings: Vec<String> =
            (0..1000)
            .map(|i| format!("str{}str", i % 100))
            .collect();
        b.iter(|| {
            for ss in strings.iter() {
                s.intern(ss);
            }
        });
        b.bytes = strings.iter().fold(0, |a, s| a + s.len()) as u64;
    }
}
<commit_msg>Allow deriving InternedString methods<commit_after>/// Strongly borrowed from TypedArena
/// Differs in growth strategy (non-doubling)
/// And only for variable-sized strings

use std::borrow::Borrow;
use std::cell::{Cell,RefCell};
use std::cmp::max;
use std::collections::LinkedList;
use std::collections::hash_map::Entry::{Occupied,Vacant};
use std::collections::hash_map::HashMap;
use std::default::Default;
use std::ops::Deref;
use std::slice;
use std::{fmt,hash,mem,ptr,str};

struct Chunk {
    start: *mut u8,
    capacity: usize,
}

impl Chunk {
    fn new(capacity: usize) -> Chunk {
        let mut slab: Vec<u8> = Vec::with_capacity(capacity);
        let start = slab.as_mut_ptr();

        // We will manually track the buffer and then drop it ourselves
        unsafe {
            mem::forget(slab);
        }

        Chunk {
            start: start,
            capacity: capacity,
        }
    }

    // Returns a pointer to the beginning of the allocated space.
    #[inline]
    fn start(&self) -> *const u8 {
        self.start as *const u8
    }

    // Returns a pointer to the end of the allocated space.
    #[inline]
    fn end(&self) -> *const u8 {
        unsafe { self.start().offset(self.capacity as isize) }
    }
}

impl Drop for Chunk {
    fn drop(&mut self) {
        // This is safe because we only ever store u8, which doesn't
        // have a destructor. This means the len doesn't matter, only
        // the capacity.
        unsafe {
            Vec::from_raw_parts(self.start, self.capacity, self.capacity);
        }
    }
}

#[allow(raw_pointer_derive)]
#[derive(Copy,Clone)]
pub struct InternedString {
    data: *const u8,
    len: usize,
}

impl InternedString {
    fn from_parts(data: *const u8, len: usize) -> InternedString {
        InternedString {
            data: data,
            len: len,
        }
    }

    pub fn from_str(s: &str) -> InternedString {
        let bytes: &[u8] = s.as_bytes();
        InternedString {
            data: bytes.as_ptr(),
            len: bytes.len(),
        }
    }

    pub fn as_slice<'s>(&self) -> &'s str {
        unsafe {
            let bytes = slice::from_raw_parts(self.data, self.len);
            str::from_utf8_unchecked(bytes)
        }
    }
}

impl fmt::Debug for InternedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl PartialEq for InternedString {
    fn eq(&self, other: &InternedString) -> bool {
        self.as_slice().eq(other.as_slice())
    }
}

impl PartialEq<str> for InternedString {
    fn eq(&self, other: &str) -> bool {
        self.as_slice().eq(other)
    }
}

impl Eq for InternedString {}

impl hash::Hash for InternedString {
    fn hash<H>(&self, state: &mut H)
        where H: hash::Hasher
    {
        self.as_slice().hash(state)
    }
}

impl Borrow<str> for InternedString {
    fn borrow(&self) -> &str {
        self.as_slice()
    }
}

impl Deref for InternedString {
    type Target = str;

    fn deref(&self) -> &str {
        self.as_slice()
    }
}

pub struct StringPool {
    start: Cell<*mut u8>,
    end: Cell<*const u8>,
    chunks: RefCell<LinkedList<Chunk>>,
    index: RefCell<HashMap<InternedString, InternedString>>,
}

static CAPACITY: usize = 10240;

impl StringPool {
    pub fn new() -> StringPool {
        StringPool{
            start: Cell::new(ptr::null_mut()),
            end: Cell::new(ptr::null()),
            chunks: RefCell::new(LinkedList::new()),
            index: RefCell::new(Default::default()),
        }
    }

    pub fn intern<'s>(&'s self, s: &str) -> &'s str {
        if s == "" { return ""; }

        let search_string = InternedString::from_str(s);

        let interned_str = match self.index.borrow_mut().entry(search_string) {
            Occupied(entry) => *entry.get(),
            Vacant(entry) => *entry.insert(self.do_intern(s)),
        };

        // The lifetime is really matched to us
        unsafe { mem::transmute(interned_str) }
    }

    fn do_intern(&self, s: &str) -> InternedString {
        self.ensure_capacity(s.len());
        self.store(s)
    }

    fn ensure_capacity(&self, str_len: usize) {
        let remaining = self.end.get() as usize - self.start.get() as usize;
        if remaining < str_len {
            self.allocate_another(max(CAPACITY, str_len))
        }
    }

    fn allocate_another(&self, capacity: usize) {
        let chunk = Chunk::new(capacity);
        self.start.set(chunk.start() as *mut u8);
        self.end.set(chunk.end());
        self.chunks.borrow_mut().push_front(chunk);
    }

    fn store(&self, s: &str) -> InternedString {
        let str_start = s.as_bytes().as_ptr();
        let str_len = s.len();

        unsafe {
            ptr::copy_nonoverlapping(str_start, self.start.get(), str_len);

            // Rebuild the string from our buffer
            let interned_str = InternedString::from_parts(self.start.get() as *const u8, str_len);

            // Increase current pointer
            self.start.set(self.start.get().offset(str_len as isize));

            interned_str
        }
    }
}

#[cfg(test)]
mod test {
    use std::borrow::ToOwned;

    use super::StringPool;

    #[test]
    fn keeps_the_same_string() {
        let s = StringPool::new();

        let interned = s.intern("hello");

        assert_eq!(interned, "hello");
    }

    #[test]
    fn does_not_reuse_the_pointer_of_the_input() {
        let s = StringPool::new();
        let input = "hello";

        let interned = s.intern(input);

        assert!(
            input.as_bytes().as_ptr() !=
            interned.as_bytes().as_ptr()
        );
    }

    #[test]
    fn reuses_the_pointer_for_repeated_input() {
        let s = StringPool::new();

        let interned1 = s.intern("world");
        let interned2 = s.intern("world");

        assert_eq!(
            interned1.as_bytes().as_ptr(),
            interned2.as_bytes().as_ptr()
        );
    }

    #[test]
    #[cfg(feature = "compile_failure")]
    fn string_cannot_outlive_the_pool() {
        let z = {
            let s = StringPool::new();
            s.intern("hello")
        };
    }

    #[test]
    fn ignores_the_lifetime_of_the_input_string() {
        let s = StringPool::new();

        let interned = {
            let allocated_string = "green".to_owned();
            s.intern(&allocated_string)
        };

        // allocated_string is gone now, but we should be able to
        // access the result value until the storage goes away.

        assert_eq!(interned, "green");
    }

    #[test]
    fn can_be_dropped_immediately() {
        StringPool::new();
    }

    fn return_populated_storage() -> (StringPool, *const u8) {
        let s = StringPool::new();
        let ptr = {
            let interned = s.intern("hello");
            interned.as_ptr()
        };
        (s, ptr)
    }

    #[test]
    fn can_return_storage_populated_with_values() {
        let (s, ptr_val) = return_populated_storage();
        let interned = s.intern("hello");
        assert_eq!(interned.as_ptr(), ptr_val);
    }
}

#[cfg(test)]
mod bench {
    use test::Bencher;

    use super::StringPool;

    #[bench]
    fn single_string(b: &mut Bencher) {
        let s = StringPool::new();
        b.iter(|| s.intern("hello"));
        b.bytes = "hello".len() as u64;
    }

    #[bench]
    fn many_unique_string(b: &mut Bencher) {
        let s = StringPool::new();

        let strings: Vec<String> =
            (0..1000)
            .map(|i| format!("str{}str", i))
            .collect();
        b.iter(|| {
            for ss in strings.iter() {
                s.intern(ss);
            }
        });
        b.bytes = strings.iter().fold(0, |a, s| a + s.len()) as u64;
    }

    #[bench]
    fn many_repeated_string(b: &mut Bencher) {
        let s = StringPool::new();

        let strings: Vec<String> =
            (0..1000)
            .map(|i| format!("str{}str", i % 100))
            .collect();
        b.iter(|| {
            for ss in strings.iter() {
                s.intern(ss);
            }
        });
        b.bytes = strings.iter().fold(0, |a, s| a + s.len()) as u64;
    }
}
<|endoftext|>