<commit_before>/* Copyright (C)  2016 nokaa <nokaa@cock.li>
 * This software is licensed under the terms of the
 * GNU Affero General Public License. You should have
 * received a copy of this license with this software.
 * The license may also be found at https://gnu.org/licenses/agpl.txt
 */

mod command;
mod cursor;
mod insert;

use rustty::{self, Event, Color};
use editor;

/// `Term` represents our client application. This allows
/// us to work with the filesystem and the UI.
pub struct Term<'a> {
    /// Represents the location of the cursor in our UI
    cursor: cursor::Cursor,
    /// Contains file information; allows us to work with
    /// the filesystem
    editor: &'a mut editor::Editor<'a>,
    /// This is the UI itself
    term: rustty::Terminal,
    /// Represents the running status
    quit: bool,
    /// Represents the topmost line in our UI
    line: usize,
    /// Represents the total number of lines in the currently
    /// open file
    total_lines: usize,
}

impl<'a> Term<'a> {
    pub fn new(editor: &'a mut editor::Editor<'a>) -> Term<'a> {
        let total_lines = editor.contents.len();
        Term {
            cursor: cursor::Cursor::new(Color::Red),
            editor: editor,
            term: rustty::Terminal::new().unwrap(),
            quit: false,
            line: 0,
            total_lines: total_lines,
        }
    }

    /// Launches the terminal.
    pub fn run(&mut self) {
        self.term[self.cursor.current_pos()].set_bg(self.cursor.color);
        let current_line = self.current_line();
        self.print_file(current_line);
        self.prompt();
        self.term.swap_buffers().unwrap();

        while !self.quit {
            let evt = self.term.get_event(100).unwrap();
            if let Some(Event::Key(ch)) = evt {
                if self.editor.command_mode() {
                    command::handle(self, ch);
                } else {
                    insert::handle(self, ch);
                }

                if self.cursor.pos.x >= self.term.cols() - 1 {
                    self.term[self.cursor.last_pos()].set_bg(Color::Default);
                    self.cursor.save_pos();
                    self.cursor.pos.x = 0;
                    self.cursor.pos.y += 1;
                }
                if self.cursor.pos.y >= self.term.rows() - 1 {
                    self.term[self.cursor.last_pos()].set_bg(Color::Default);
                    self.cursor.save_pos();
                    self.cursor.pos.x = 0;
                    self.cursor.pos.y = 0;
                }

                self.term[self.cursor.last_pos()].set_bg(Color::Default);
                self.term[self.cursor.current_pos()].set_bg(self.cursor.color);
                self.term.swap_buffers().unwrap();
            }
        }
    }

    /// Prints our prompt to the UI.
    pub fn prompt(&mut self) {
        let w = self.term.cols();
        let h = self.term.rows();

        for i in 0..w {
            self.term[(i, h - 2)].set_bg(Color::Red);
        }

        for (i, c) in self.editor.filename().chars().enumerate() {
            self.term[(i, h - 2)].set_ch(c);
        }
    }

    /// Prints our editor's contents to the UI.
    fn print_file(&mut self, start: usize) {
        let w = self.term.cols();
        let mut i = 0;
        let mut j = 0;
        for line in &self.editor.contents {
            if i < start {
                i += 1;
                continue;
            }

            for b in line {
                self.term[(j, i - start)].set_ch(*b as char);
                j += 1;
            }
            while j < w {
                self.term[(j, i - start)].set_ch(' ');
                j += 1;
            }

            i += 1;
            j = 0;
            if i - start == self.term.rows() - 2 {
                break;
            }
        }
    }

    pub fn move_cursor(&mut self, direction: cursor::Direction) {
        match direction {
            cursor::Direction::Up => {
                if self.cursor.pos.y != 0 {
                    self.cursor.save_pos();
                    self.cursor.pos.y -= 1;
                } else if self.current_line() != 0 {
                    let current_line = self.current_line();
                    self.set_current_line(current_line - 1);
                    self.redraw_file();
                }
            }
            cursor::Direction::Down => {
                if self.total_lines() <= self.term.rows() - 3 {
                    if self.cursor.pos.y != self.term.rows() - 3 &&
                       self.cursor.pos.y < self.total_lines() - 1
                    {
                        self.cursor.save_pos();
                        self.cursor.pos.y += 1;
                    }
                } else {
                    if self.cursor.pos.y != self.term.rows() - 3 {
                        self.cursor.save_pos();
                        self.cursor.pos.y += 1;
                    } else if self.cursor.pos.y + self.current_line() <
                              self.total_lines() - 1
                    {
                        let current_line = self.current_line();
                        self.set_current_line(current_line + 1);
                        self.redraw_file();
                    }
                }
            }
            cursor::Direction::Left => {
                if self.cursor.pos.x != 0 {
                    self.cursor.save_pos();
                    self.cursor.pos.x -= 1;
                }
            }
            cursor::Direction::Right => {
                let curr = self.cursor.pos.y + self.current_line();
                if self.cursor.pos.x != self.editor.contents[curr].len() - 1 {
                    self.cursor.save_pos();
                    self.cursor.pos.x += 1;
                }
            }
        }
    }

    /// Changes the value of the `quit` attribute to `true`.
    pub fn quit(&mut self) {
        self.quit = true;
    }

    /// Returns the total lines in our file
    pub fn total_lines(&self) -> usize {
        self.total_lines
    }

    /// Allows us to modify the total number of lines
    /// in our file, e.g. if a new line is added.
    pub fn set_total_lines(&mut self, change: isize) {
        let new = self.total_lines as isize + change;
        self.total_lines = new as usize;
    }

    /// Gives the current line at the top of our UI
    pub fn current_line(&self) -> usize {
        self.line
    }

    /// Allows us to change what the topmost line in the UI is
    pub fn set_current_line(&mut self, line: usize) {
        self.line = line;
    }

    /// This function redraws the portion of our UI displaying
    /// file contents. E.g. this is called when the current line
    /// changes.
    pub fn redraw_file(&mut self) {
        let current_line = self.current_line();
        self.print_file(current_line);
    }
}
<commit_msg>Implement basic line-wrapping<commit_after>/* Copyright (C)  2016 nokaa <nokaa@cock.li>
 * This software is licensed under the terms of the
 * GNU Affero General Public License. You should have
 * received a copy of this license with this software.
 * The license may also be found at https://gnu.org/licenses/agpl.txt
 */

mod command;
mod cursor;
mod insert;

use rustty::{self, Event, Color};
use editor;

/// `Term` represents our client application. This allows
/// us to work with the filesystem and the UI.
pub struct Term<'a> {
    /// Represents the location of the cursor in our UI
    cursor: cursor::Cursor,
    /// Contains file information; allows us to work with
    /// the filesystem
    editor: &'a mut editor::Editor<'a>,
    /// This is the UI itself
    term: rustty::Terminal,
    /// Represents the running status
    quit: bool,
    /// Represents the topmost line in our UI
    line: usize,
    /// Represents the total number of lines in the currently
    /// open file
    total_lines: usize,
}

impl<'a> Term<'a> {
    pub fn new(editor: &'a mut editor::Editor<'a>) -> Term<'a> {
        let total_lines = editor.contents.len();
        Term {
            cursor: cursor::Cursor::new(Color::Red),
            editor: editor,
            term: rustty::Terminal::new().unwrap(),
            quit: false,
            line: 0,
            total_lines: total_lines,
        }
    }

    /// Launches the terminal.
    pub fn run(&mut self) {
        self.term[self.cursor.current_pos()].set_bg(self.cursor.color);
        let current_line = self.current_line();
        self.print_file(current_line);
        self.prompt();
        self.term.swap_buffers().unwrap();

        while !self.quit {
            let evt = self.term.get_event(100).unwrap();
            if let Some(Event::Key(ch)) = evt {
                if self.editor.command_mode() {
                    command::handle(self, ch);
                } else {
                    insert::handle(self, ch);
                }

                if self.cursor.pos.x >= self.term.cols() - 1 {
                    self.term[self.cursor.last_pos()].set_bg(Color::Default);
                    self.cursor.save_pos();
                    self.cursor.pos.x = 0;
                    self.cursor.pos.y += 1;
                }
                if self.cursor.pos.y >= self.term.rows() - 1 {
                    self.term[self.cursor.last_pos()].set_bg(Color::Default);
                    self.cursor.save_pos();
                    self.cursor.pos.x = 0;
                    self.cursor.pos.y = 0;
                }

                self.term[self.cursor.last_pos()].set_bg(Color::Default);
                self.term[self.cursor.current_pos()].set_bg(self.cursor.color);
                self.term.swap_buffers().unwrap();
            }
        }
    }

    /// Prints our prompt to the UI.
    pub fn prompt(&mut self) {
        let w = self.term.cols();
        let h = self.term.rows();

        for i in 0..w {
            self.term[(i, h - 2)].set_bg(Color::Red);
        }

        for (i, c) in self.editor.filename().chars().enumerate() {
            self.term[(i, h - 2)].set_ch(c);
        }
    }

    /// Prints our editor's contents to the UI.
    fn print_file(&mut self, start: usize) {
        let w = self.term.cols();
        let mut i = 0;
        let mut j = 0;
        for line in &self.editor.contents {
            if i < start {
                i += 1;
                continue;
            }

            for b in line {
                self.term[(j, i - start)].set_ch(*b as char);
                j += 1;
                // If the current line is longer than term width, we move
                // to a new line.
                //
                // TODO(nokaa): If line-width is greater than term width,
                // we should insert a new line our editor array
                if j == w {
                    j = 0;
                    i += 1;
                }
            }
            while j < w {
                self.term[(j, i - start)].set_ch(' ');
                j += 1;
            }

            i += 1;
            j = 0;
            if i - start == self.term.rows() - 2 {
                break;
            }
        }
    }

    pub fn move_cursor(&mut self, direction: cursor::Direction) {
        match direction {
            cursor::Direction::Up => {
                if self.cursor.pos.y != 0 {
                    self.cursor.save_pos();
                    self.cursor.pos.y -= 1;
                } else if self.current_line() != 0 {
                    let current_line = self.current_line();
                    self.set_current_line(current_line - 1);
                    self.redraw_file();
                }
            }
            cursor::Direction::Down => {
                if self.total_lines() <= self.term.rows() - 3 {
                    if self.cursor.pos.y != self.term.rows() - 3 &&
                       self.cursor.pos.y < self.total_lines() - 1
                    {
                        self.cursor.save_pos();
                        self.cursor.pos.y += 1;
                    }
                } else {
                    if self.cursor.pos.y != self.term.rows() - 3 {
                        self.cursor.save_pos();
                        self.cursor.pos.y += 1;
                    } else if self.cursor.pos.y + self.current_line() <
                              self.total_lines() - 1
                    {
                        let current_line = self.current_line();
                        self.set_current_line(current_line + 1);
                        self.redraw_file();
                    }
                }
            }
            cursor::Direction::Left => {
                if self.cursor.pos.x != 0 {
                    self.cursor.save_pos();
                    self.cursor.pos.x -= 1;
                }
            }
            cursor::Direction::Right => {
                let curr = self.cursor.pos.y + self.current_line();
                if self.cursor.pos.x != self.editor.contents[curr].len() - 1 {
                    self.cursor.save_pos();
                    self.cursor.pos.x += 1;
                }
            }
        }
    }

    /// Changes the value of the `quit` attribute to `true`.
    pub fn quit(&mut self) {
        self.quit = true;
    }

    /// Returns the total lines in our file
    pub fn total_lines(&self) -> usize {
        self.total_lines
    }

    /// Allows us to modify the total number of lines
    /// in our file, e.g. if a new line is added.
    pub fn set_total_lines(&mut self, change: isize) {
        let new = self.total_lines as isize + change;
        self.total_lines = new as usize;
    }

    /// Gives the current line at the top of our UI
    pub fn current_line(&self) -> usize {
        self.line
    }

    /// Allows us to change what the topmost line in the UI is
    pub fn set_current_line(&mut self, line: usize) {
        self.line = line;
    }

    /// This function redraws the portion of our UI displaying
    /// file contents. E.g. this is called when the current line
    /// changes.
    pub fn redraw_file(&mut self) {
        let current_line = self.current_line();
        self.print_file(current_line);
    }
}
<|endoftext|>


<commit_before>// Tests for calx_ecs are off-crate because the Ecs macro expects to
// find Ecs stuff with an absolute crate path.

#[derive(Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)]
pub struct Desc {
    name: String,
    icon: usize,
}

#[derive(Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)]
pub struct Pos {
    x: i32,
    y: i32,
}

Ecs! {
    desc: Desc,
    pos: Pos,
}

#[test]
fn test_ecs() {
    let mut ecs = Ecs::new();

    let e1 = ecs.make();
    assert!(ecs.contains(e1));

    assert!(!ecs.pos.contains(e1));
    ecs.pos.insert(e1, Pos { x: 3, y: 4 });
    assert_eq!(ecs.pos[e1], Pos { x: 3, y: 4 });

    Desc { name: "Orc".to_string(), icon: 8 }.add_to(&mut ecs, e1);
    assert_eq!(ecs.desc[e1].name, "Orc");

    ecs.remove(e1);
    assert!(!ecs.pos.contains(e1));
    assert!(!ecs.contains(e1));

    let e2 = ecs.make();
    assert!(e2 != e1);
}
<commit_msg>Unit test ECS loadout<commit_after>// Tests for calx_ecs are off-crate because the Ecs macro expects to
// find Ecs stuff with an absolute crate path.

#[derive(Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)]
pub struct Desc {
    name: String,
    icon: usize,
}

#[derive(Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)]
pub struct Pos {
    x: i32,
    y: i32,
}

Ecs! {
    desc: Desc,
    pos: Pos,
}

#[test]
fn test_ecs() {
    let mut ecs = Ecs::new();

    let e1 = ecs.make();
    assert!(ecs.contains(e1));

    assert!(!ecs.pos.contains(e1));
    ecs.pos.insert(e1, Pos { x: 3, y: 4 });
    assert_eq!(ecs.pos[e1], Pos { x: 3, y: 4 });

    Desc { name: "Orc".to_string(), icon: 8 }.add_to(&mut ecs, e1);
    assert_eq!(ecs.desc[e1].name, "Orc");

    ecs.remove(e1);
    assert!(!ecs.pos.contains(e1));
    assert!(!ecs.contains(e1));

    let e2 = ecs.make();
    assert!(e2 != e1);

    // Use the loadout system to create a form.
    let form = loadout! [
        Desc { name: "Critter".to_string(), icon: 10 },
        Pos { x: 12, y: 15 }
    ];

    // Then instantiate an entity with that form.
    let instance = ecs.make();
    for comp in form.iter() {
        comp.add_to(&mut ecs, instance);
    }
    assert!(ecs.pos[instance] == Pos { x: 12, y: 15 });

}
<|endoftext|>


<commit_before>use std;
use std::io::{Chars, Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::poll;
use nix::sys::signal;
use nix::sys::termios;

use consts::{self, KeyPress};
use ::Result;
use ::error;
use super::RawReader;

pub type Mode = termios::Termios;
const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
fn get_columns() -> usize {
    let (cols, _) = get_win_size();
    cols
}

/// Try to get the number of rows in the current terminal,
/// or assume 24 if it fails.
fn get_rows() -> usize {
    let (_, rows) = get_win_size();
    rows
}

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;
    use libc::c_ushort;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            let mut unsupported = false;
            for iter in &UNSUPPORTED_TERM {
                unsupported = (*iter).eq_ignore_ascii_case(&term)
            }
            unsupported
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

/// Enable RAW mode for the terminal.
pub fn enable_raw_mode() -> Result<Mode> {
    use nix::errno::Errno::ENOTTY;
    use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
                            /* OPOST, */ VMIN, VTIME};
    if !is_a_tty(STDIN_FILENO) {
        try!(Err(nix::Error::from_errno(ENOTTY)));
    }
    let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
    let mut raw = original_mode;
    // disable BREAK interrupt, CR to NL conversion on input,
    // input parity check, strip high bit (bit 8), output flow control
    raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    // we don't want raw output, it turns newlines into straight linefeeds
    // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
    raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
    // disable echoing, canonical mode, extended input processing and signals
    raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
    raw.c_cc[VMIN] = 1; // One character-at-a-time input
    raw.c_cc[VTIME] = 0; // with blocking read
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &raw));
    Ok(original_mode)
}

/// Disable RAW mode for the terminal.
pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &original_mode));
    Ok(())
}

fn clear_screen(w: &mut Write) -> Result<()> {
    try!(w.write_all(b"\x1b[H\x1b[2J"));
    try!(w.flush());
    Ok(())
}

/// Console input reader
pub struct PosixRawReader {
    chars: Chars<std::io::Stdin>,
}

impl PosixRawReader {
    pub fn new(stdin: std::io::Stdin) -> Result<PosixRawReader> {
        Ok(PosixRawReader { chars: stdin.chars() })
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

impl RawReader for PosixRawReader {
    fn next_key(&mut self) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if key == KeyPress::Esc {
            let mut fds = [poll::PollFd::new(STDIN_FILENO, poll::POLLIN, poll::EventFlags::empty())];
            match poll::poll(&mut fds, 500) {
                Ok(n) if n == 0 => {
                    // single escape
                },
                Ok(_) => {
                    // escape sequence
                    key = try!(self.escape_sequence())
                },
                // Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                Err(e) => return Err(e.into()),
            }
        }
        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlags::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: libc::c_int) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone, Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl PosixTerminal {
    pub fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    pub fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    pub fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Get the number of columns in the current terminal.
    pub fn get_columns(&self) -> usize {
        get_columns()
    }

    /// Get the number of rows in the current terminal.
    pub fn get_rows(&self) -> usize {
        get_rows()
    }

    /// Create a RAW reader
    pub fn create_reader(&self) -> Result<PosixRawReader> {
        PosixRawReader::new(std::io::stdin())
    }

    /// Check if a SIGWINCH signal has been received
    pub fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    pub fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        clear_screen(w)
    }
}
<commit_msg>Rustfmt<commit_after>use std;
use std::io::{Chars, Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::poll;
use nix::sys::signal;
use nix::sys::termios;

use consts::{self, KeyPress};
use ::Result;
use ::error;
use super::RawReader;

pub type Mode = termios::Termios;
const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
fn get_columns() -> usize {
    let (cols, _) = get_win_size();
    cols
}

/// Try to get the number of rows in the current terminal,
/// or assume 24 if it fails.
fn get_rows() -> usize {
    let (_, rows) = get_win_size();
    rows
}

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;
    use libc::c_ushort;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            let mut unsupported = false;
            for iter in &UNSUPPORTED_TERM {
                unsupported = (*iter).eq_ignore_ascii_case(&term)
            }
            unsupported
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

/// Enable RAW mode for the terminal.
pub fn enable_raw_mode() -> Result<Mode> {
    use nix::errno::Errno::ENOTTY;
    use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
                            /* OPOST, */ VMIN, VTIME};
    if !is_a_tty(STDIN_FILENO) {
        try!(Err(nix::Error::from_errno(ENOTTY)));
    }
    let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
    let mut raw = original_mode;
    // disable BREAK interrupt, CR to NL conversion on input,
    // input parity check, strip high bit (bit 8), output flow control
    raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    // we don't want raw output, it turns newlines into straight linefeeds
    // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
    raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
    // disable echoing, canonical mode, extended input processing and signals
    raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
    raw.c_cc[VMIN] = 1; // One character-at-a-time input
    raw.c_cc[VTIME] = 0; // with blocking read
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &raw));
    Ok(original_mode)
}

/// Disable RAW mode for the terminal.
pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &original_mode));
    Ok(())
}

fn clear_screen(w: &mut Write) -> Result<()> {
    try!(w.write_all(b"\x1b[H\x1b[2J"));
    try!(w.flush());
    Ok(())
}

/// Console input reader
pub struct PosixRawReader {
    chars: Chars<std::io::Stdin>,
}

impl PosixRawReader {
    pub fn new(stdin: std::io::Stdin) -> Result<PosixRawReader> {
        Ok(PosixRawReader { chars: stdin.chars() })
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

impl RawReader for PosixRawReader {
    fn next_key(&mut self) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if key == KeyPress::Esc {
            let mut fds =
                [poll::PollFd::new(STDIN_FILENO, poll::POLLIN, poll::EventFlags::empty())];
            match poll::poll(&mut fds, 500) {
                Ok(n) if n == 0 => {
                    // single escape
                }
                Ok(_) => {
                    // escape sequence
                    key = try!(self.escape_sequence())
                }
                // Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                Err(e) => return Err(e.into()),
            }
        }
        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlags::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: libc::c_int) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone, Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl PosixTerminal {
    pub fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    pub fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    pub fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Get the number of columns in the current terminal.
    pub fn get_columns(&self) -> usize {
        get_columns()
    }

    /// Get the number of rows in the current terminal.
    pub fn get_rows(&self) -> usize {
        get_rows()
    }

    /// Create a RAW reader
    pub fn create_reader(&self) -> Result<PosixRawReader> {
        PosixRawReader::new(std::io::stdin())
    }

    /// Check if a SIGWINCH signal has been received
    pub fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    pub fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        clear_screen(w)
    }
}
<|endoftext|>


<commit_before>pub extern crate openssl;
extern crate postgres;

use openssl::error::ErrorStack;
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::ssl::{ConnectConfiguration, SslConnector, SslMethod, SslStream};
use postgres::tls::{Stream, TlsHandshake, TlsStream};
use std::error::Error;
use std::fmt;
use std::io::{self, Read, Write};

#[cfg(test)]
mod test;

pub struct OpenSsl {
    connector: SslConnector,
    config: Box<Fn(&mut ConnectConfiguration) -> Result<(), ErrorStack> + Sync + Send>,
}

impl fmt::Debug for OpenSsl {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("OpenSsl").finish()
    }
}

impl OpenSsl {
    pub fn new() -> Result<OpenSsl, ErrorStack> {
        let connector = SslConnector::builder(SslMethod::tls())?.build();
        Ok(OpenSsl::with_connector(connector))
    }

    pub fn with_connector(connector: SslConnector) -> OpenSsl {
        OpenSsl {
            connector,
            config: Box::new(|_| Ok(())),
        }
    }

    pub fn callback<F>(&mut self, f: F)
    where
        F: Fn(&mut ConnectConfiguration) -> Result<(), ErrorStack> + 'static + Sync + Send,
    {
        self.config = Box::new(f);
    }
}

impl TlsHandshake for OpenSsl {
    fn tls_handshake(
        &self,
        domain: &str,
        stream: Stream,
    ) -> Result<Box<TlsStream>, Box<Error + Sync + Send>> {
        let mut ssl = self.connector.configure()?;
        (self.config)(&mut ssl)?;
        let stream = ssl.connect(domain, stream)?;

        Ok(Box::new(OpenSslStream(stream)))
    }
}

#[derive(Debug)]
struct OpenSslStream(SslStream<Stream>);

impl Read for OpenSslStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl Write for OpenSslStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl TlsStream for OpenSslStream {
    fn get_ref(&self) -> &Stream {
        self.0.get_ref()
    }

    fn get_mut(&mut self) -> &mut Stream {
        self.0.get_mut()
    }

    fn tls_server_end_point(&self) -> Option<Vec<u8>> {
        let cert = self.0.ssl().peer_certificate()?;
        let algo_nid = cert.signature_algorithm().object().nid();
        let signature_algorithms = algo_nid.signature_algorithms()?;

        let md = match signature_algorithms.digest {
            Nid::MD5 | Nid::SHA1 => MessageDigest::sha256(),
            nid => MessageDigest::from_nid(nid)?,
        };

        let digest = cert.digest(md).ok()?;

        Some(digest.to_vec())
    }
}
<commit_msg>Implement tls-unique for postgres-openssl<commit_after>pub extern crate openssl;
extern crate postgres;

use openssl::error::ErrorStack;
use openssl::ssl::{ConnectConfiguration, SslConnector, SslMethod, SslRef, SslStream};
use postgres::tls::{Stream, TlsHandshake, TlsStream};
use std::error::Error;
use std::fmt;
use std::io::{self, Read, Write};

#[cfg(test)]
mod test;

pub struct OpenSsl {
    connector: SslConnector,
    config: Box<Fn(&mut ConnectConfiguration) -> Result<(), ErrorStack> + Sync + Send>,
}

impl fmt::Debug for OpenSsl {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("OpenSsl").finish()
    }
}

impl OpenSsl {
    pub fn new() -> Result<OpenSsl, ErrorStack> {
        let connector = SslConnector::builder(SslMethod::tls())?.build();
        Ok(OpenSsl::with_connector(connector))
    }

    pub fn with_connector(connector: SslConnector) -> OpenSsl {
        OpenSsl {
            connector,
            config: Box::new(|_| Ok(())),
        }
    }

    pub fn callback<F>(&mut self, f: F)
    where
        F: Fn(&mut ConnectConfiguration) -> Result<(), ErrorStack> + 'static + Sync + Send,
    {
        self.config = Box::new(f);
    }
}

impl TlsHandshake for OpenSsl {
    fn tls_handshake(
        &self,
        domain: &str,
        stream: Stream,
    ) -> Result<Box<TlsStream>, Box<Error + Sync + Send>> {
        let mut ssl = self.connector.configure()?;
        (self.config)(&mut ssl)?;
        let stream = ssl.connect(domain, stream)?;

        Ok(Box::new(OpenSslStream(stream)))
    }
}

#[derive(Debug)]
struct OpenSslStream(SslStream<Stream>);

impl Read for OpenSslStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl Write for OpenSslStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl TlsStream for OpenSslStream {
    fn get_ref(&self) -> &Stream {
        self.0.get_ref()
    }

    fn get_mut(&mut self) -> &mut Stream {
        self.0.get_mut()
    }

    fn tls_unique(&self) -> Option<Vec<u8>> {
        let f = if self.0.ssl().session_reused() {
            SslRef::peer_finished
        } else {
            SslRef::finished
        };

        let len = f(self.0.ssl(), &mut []);
        let mut buf = vec![0; len];
        f(self.0.ssl(), &mut buf);
        Some(buf)
    }
}
<|endoftext|>


<commit_before>// todo: documentation
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

impl Point {
    pub fn new(x: f64, y: f64) -> Self {
        Point { x, y }
    }

    /// Returns the distance between this `Point` and the given `Point`.
    pub fn distance(&self, other: Self) -> f64 {
        ((self.x - other.x).powf(2.) + (self.y - other.y).powf(2.)).sqrt()
    }
}

impl From<f64> for Point {
    fn from(t: f64) -> Self {
        Point::new(t, t)
    }
}

impl From<i32> for Point {
    fn from(t: i32) -> Self {
        Point::new(t as f64, t as f64)
    }
}

impl From<(f64, f64)> for Point {
    fn from(t: (f64, f64)) -> Self {
        Point::new(t.0, t.1)
    }
}

impl From<(i32, i32)> for Point {
    fn from(s: (i32, i32)) -> Point {
        Point::from((s.0 as f64, s.1 as f64))
    }
}

#[test]
fn test_distance() {
    const EXPECTED_RESULT: f64 = 9.48683;
    const ERROR_MARGIN: f64 = 0.00001;

    let point_positive = Point::new(1., 5.);
    let point_negative = Point::new(-2., -4.);

    assert!(((point_positive.distance(point_negative) - EXPECTED_RESULT).abs() < ERROR_MARGIN));
    assert!(((point_negative.distance(point_positive) - EXPECTED_RESULT).abs() < ERROR_MARGIN));
}
<commit_msg>`impl Sub for Point`<commit_after>use core::ops::Sub;

// todo: documentation
#[derive(Copy, Clone, Default, Debug, PartialEq)]

pub struct Point {
    pub x: f64,
    pub y: f64,
}

impl Point {
    pub fn new(x: f64, y: f64) -> Self {
        Point { x, y }
    }

    /// Returns the distance between this `Point` and the given `Point`.
    pub fn distance(&self, other: Self) -> f64 {
        ((self.x - other.x).powf(2.) + (self.y - other.y).powf(2.)).sqrt()
    }
}

impl Sub for Point {
    type Output = Self;

    fn sub(self, other: Self) -> Self {
        Self {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

impl From<f64> for Point {
    fn from(t: f64) -> Self {
        Point::new(t, t)
    }
}

impl From<i32> for Point {
    fn from(t: i32) -> Self {
        Point::new(t as f64, t as f64)
    }
}

impl From<(f64, f64)> for Point {
    fn from(t: (f64, f64)) -> Self {
        Point::new(t.0, t.1)
    }
}

impl From<(i32, i32)> for Point {
    fn from(s: (i32, i32)) -> Point {
        Point::from((s.0 as f64, s.1 as f64))
    }
}

#[test]
fn test_distance() {
    const EXPECTED_RESULT: f64 = 9.48683;
    const ERROR_MARGIN: f64 = 0.00001;

    let point_positive = Point::new(1., 5.);
    let point_negative = Point::new(-2., -4.);

    assert!(((point_positive.distance(point_negative) - EXPECTED_RESULT).abs() < ERROR_MARGIN));
    assert!(((point_negative.distance(point_positive) - EXPECTED_RESULT).abs() < ERROR_MARGIN));
}

#[test]
fn test_sub() {
    const EXPECTED_RESULT: Point = Point { x: -3., y: 5. };
    const ERROR_MARGIN: f64 = 0.00001;

    let left_side = Point::new(5., 7.);
    let right_side = Point::new(8., 2.);

    let result = left_side - right_side;

    assert!((result.x - EXPECTED_RESULT.x).abs() < ERROR_MARGIN);
    assert!((result.y - EXPECTED_RESULT.y).abs() < ERROR_MARGIN);
}
<|endoftext|>


<commit_before>use std::mem;
use std::slice::raw;
use numvec::{Vec2, Vec2f};
use super::types::{WadCoord, WadInfo, WadName, ChildId};
use std::ascii::ASCII_UPPER_MAP;

pub enum WadType { Initial, Patch }

static IWAD_HEADER: &'static [u8] = b"IWAD";
static PWAD_HEADER: &'static [u8] = b"PWAD";

pub fn wad_type_from_info(wad_info : &WadInfo) -> Option<WadType> {
    let id : &[u8] = &wad_info.identifier;
    match id {
        IWAD_HEADER => Some(Initial),
        PWAD_HEADER => Some(Patch),
        _           => None
    }
}

pub fn read_binary<T : Copy, R : Reader>(reader : &mut R) -> T {
    let mut loaded : T = unsafe { mem::zeroed() };
    let size = mem::size_of::<T>();
    unsafe {
        raw::mut_buf_as_slice(
            &mut loaded as *mut T as *mut u8, size,
            |buf| { reader.read_at_least(size, buf).unwrap() });
    };
    loaded
}

pub fn name_toupper(name: &[u8]) -> Vec<u8> {
    let mut name = name.to_vec();
    for c in name.iter_mut() {
        *c = ASCII_UPPER_MAP[*c as uint];
    }
    name
}

pub fn name_from_str(name: &str) -> [u8, ..8] {
    let bytes = name.as_bytes();
    assert!(bytes.len() <= 8);

    let mut name = [0u8, ..8];
    for i_byte in range(0, bytes.len()) {
        name[i_byte] = bytes[i_byte];
    }
    name
}

pub fn name_eq(name1: &WadName, name2: &[u8]) -> bool {
    for i_byte in range(0, 8) {
        if i_byte == name1.len() { return i_byte == name2.len(); }
        if i_byte == name2.len() { return i_byte == name1.len(); }

        if name1[i_byte] != name2[i_byte] { return false; }
        if name1[i_byte] == 0 { return true; }
    }
    true
}

pub fn name_eq_str(name: &WadName, str_name: &str) -> bool {
    name_eq(name, &name_from_str(str_name))
}

pub fn is_untextured(name: &WadName) -> bool { name[0] == b'-' && name[1] == 0 }
pub fn is_sky_flat(name: &WadName) -> bool { name_eq(name, b"F_SKY1\0\0\0") }

pub fn from_wad_height(x: WadCoord) -> f32 { (x as f32) / 100.0 }

pub fn from_wad_coords(x: WadCoord, y: WadCoord) -> Vec2f {
    Vec2::new(-from_wad_height(x), from_wad_height(y))
}

pub fn parse_child_id(id: ChildId) -> (uint, bool) {
    ((id & 0x7fff) as uint, id & 0x8000 != 0)
}
<commit_msg>Added hardcoded animated flat names.<commit_after>use std::mem;
use std::slice::raw;
use numvec::{Vec2, Vec2f};
use super::types::{WadCoord, WadInfo, WadName, ChildId};
use std::ascii::ASCII_UPPER_MAP;

pub enum WadType { Initial, Patch }

static IWAD_HEADER: &'static [u8] = b"IWAD";
static PWAD_HEADER: &'static [u8] = b"PWAD";

pub fn wad_type_from_info(wad_info : &WadInfo) -> Option<WadType> {
    let id : &[u8] = &wad_info.identifier;
    match id {
        IWAD_HEADER => Some(Initial),
        PWAD_HEADER => Some(Patch),
        _           => None
    }
}

pub fn read_binary<T : Copy, R : Reader>(reader : &mut R) -> T {
    let mut loaded : T = unsafe { mem::zeroed() };
    let size = mem::size_of::<T>();
    unsafe {
        raw::mut_buf_as_slice(
            &mut loaded as *mut T as *mut u8, size,
            |buf| { reader.read_at_least(size, buf).unwrap() });
    };
    loaded
}

pub fn name_toupper(name: &[u8]) -> Vec<u8> {
    let mut name = name.to_vec();
    for c in name.iter_mut() {
        *c = ASCII_UPPER_MAP[*c as uint];
    }
    name
}

static NUKAGE_FRAMES: &'static [&'static [u8]] =
    &[b"NUKAGE1\0", b"NUKAGE2\0", b"NUKAGE3\0"];
static FWATER_FRAMES: &'static [&'static [u8]] =
    &[b"FWATER1\0", b"FWATER2\0", b"FWATER3\0", b"FWATER4\0"];
static SWATER_FRAMES: &'static [&'static [u8]] =
    &[b"SWATER1\0", b"SWATER2\0", b"SWATER3\0", b"SWATER4\0"];
static LAVA_FRAMES: &'static [&'static [u8]] =
    &[b"LAVA1\0\0\0", b"LAVA2\0\0\0", b"LAVA3\0\0\0", b"LAVA4\0\0\0"];
static BLOOD_FRAMES: &'static [&'static [u8]] =
    &[b"BLOOD1\0\0", b"BLOOD2\0\0", b"BLOOD3\0\0"];
static RROCK05_FRAMES: &'static [&'static [u8]] =
    &[b"RROCK05\0", b"RROCK06\0", b"RROCK07\0", b"RROCK08\0"];
static SLIME01_FRAMES: &'static [&'static [u8]] =
    &[b"SLIME01\0", b"SLIME02\0", b"SLIME03\0", b"SLIME04\0"];
static SLIME05_FRAMES: &'static [&'static [u8]] =
    &[b"SLIME05\0", b"SLIME06\0", b"SLIME07\0", b"SLIME08\0"];
static SLIME09_FRAMES: &'static [&'static [u8]] =
    &[b"SLIME09\0", b"SLIME10\0", b"SLIME11\0", b"SLIME12\0"];
static ANIMATED_FLATS: &'static [&'static [&'static [u8]]] = [
    NUKAGE_FRAMES, FWATER_FRAMES, SWATER_FRAMES, LAVA_FRAMES, BLOOD_FRAMES,
    RROCK05_FRAMES, SLIME01_FRAMES, SLIME05_FRAMES, SLIME09_FRAMES,
];

pub fn flat_frame_names(name: &[u8])
        -> Option<(uint, &'static [&'static [u8]])> {
    for animation in ANIMATED_FLATS.iter() {
        for (i_frame, frame_name) in animation.iter().enumerate() {
            if *frame_name == name {
                return Some((i_frame, *animation))
            }
        }
    }
    None
}

pub fn name_from_str(name: &str) -> [u8, ..8] {
    let bytes = name.as_bytes();
    assert!(bytes.len() <= 8);

    let mut name = [0u8, ..8];
    for i_byte in range(0, bytes.len()) {
        name[i_byte] = bytes[i_byte];
    }
    name
}

pub fn name_eq(name1: &WadName, name2: &[u8]) -> bool {
    for i_byte in range(0, 8) {
        if i_byte == name1.len() { return i_byte == name2.len(); }
        if i_byte == name2.len() { return i_byte == name1.len(); }

        if name1[i_byte] != name2[i_byte] { return false; }
        if name1[i_byte] == 0 { return true; }
    }
    true
}

pub fn name_eq_str(name: &WadName, str_name: &str) -> bool {
    name_eq(name, &name_from_str(str_name))
}

pub fn is_untextured(name: &WadName) -> bool { name[0] == b'-' && name[1] == 0 }
pub fn is_sky_flat(name: &WadName) -> bool { name_eq(name, b"F_SKY1\0\0\0") }

pub fn from_wad_height(x: WadCoord) -> f32 { (x as f32) / 100.0 }

pub fn from_wad_coords(x: WadCoord, y: WadCoord) -> Vec2f {
    Vec2::new(-from_wad_height(x), from_wad_height(y))
}

pub fn parse_child_id(id: ChildId) -> (uint, bool) {
    ((id & 0x7fff) as uint, id & 0x8000 != 0)
}
<|endoftext|>


<commit_before>#![crate_type = "lib"]
#![crate_name = "coinaddress"]

//! Functions for validating the base58 hash checksums, including specifically
//! the bitcoin and litecoin addresses.

#![feature(test)]
#![feature(rustc_private)]

extern crate num;
extern crate rustc;
extern crate test;

use num::bigint::BigUint;
use num::Zero;
use std::num::{from_uint, from_int, ToPrimitive};
use rustc::util::sha2::{Sha256, Digest};

#[derive(PartialEq, Debug)]
pub enum ValidationError {
    /// Given address is too short to be valid
    TooShort,
    /// Encoding is not a valid base58
    InvalidEncoding,
    /// Computed hash does not match the embedded one
    HashMismatch,

    // currency specific
    /// This address is not a bitcoin address (testnet or real).
    /// May happen when attempting to validate btc address
    NotBitcoin,
    /// This address is not a litecoin address.
    /// May happen when attempting to validate ltc address
    NotLitecoin,
}

static BASE58_CHARS: &'static str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

fn decode_base58(bc: &str) -> Option<BigUint> {
    let mut res: BigUint = Zero::zero();
    let b58: BigUint = from_int(58).unwrap();

    for c in bc.chars() {
        match BASE58_CHARS.find(c) {
            None => {
                return None
            },
            Some(x) => {
                res = res * &b58 + from_uint::<BigUint>(x).unwrap();
            }
        }
    }
    Some(res)
}

fn bigint_to_bytes(n: BigUint) -> Vec<u8> {
    let mut res: Vec<u8> = Vec::new();
    let mut tmp = n;
    let b256: BigUint = from_int(256).unwrap();

    while !tmp.is_zero() {
        res.insert(0, (&tmp % &b256).to_u8().unwrap());
        tmp = tmp / &b256;
    }

    if res.len() == 0 {
        return vec!(0);
    }

    res
}

fn pad_to(v: Vec<u8>, len: usize) -> Vec<u8> {
    let mut tmp: Vec<u8> = v;
    while tmp.len() < len {
        tmp.insert(0, 0)
    }
    tmp
}

fn double_sha256(chunk: &[u8]) -> Vec<u8> {
    let mut hash = Sha256::new();
    hash.input(chunk);
    let mut hash2 = Sha256::new();
    hash2.input(&hash.result_bytes()[..]);
    hash2.result_bytes()
}

/// Validate provided generic base58 hash.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_base58_hash(addr: &str) -> Result<usize, ValidationError> {
    if addr.len() == 0 {
        return Err(ValidationError::TooShort);
    }

    let big = match decode_base58(addr) {
        None => return Err(ValidationError::InvalidEncoding),
        Some(x) => x
    };
    let bytes = bigint_to_bytes(big);
    let padded = pad_to(bytes, 25);

    let hash = double_sha256(&padded[0 .. padded.len() - 4]);
    let short_hash = &hash[0..4];
    let known = &padded[padded.len()-4 .. padded.len()];
    if &short_hash[..] == known {
        Ok(padded[0] as usize)
    } else {
        Err(ValidationError::HashMismatch)
    }
}

/// Validate bitcoin address checksum.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_btc_address(addr: &str) -> Result<usize, ValidationError> {
    match validate_base58_hash(addr) {
        Ok(0) => Ok(0),      // real address
        Ok(5) => Ok(5),      // script hash
        Ok(111) => Ok(111),  // testnet address
        Ok(_) => Err(ValidationError::NotBitcoin),
        Err(x) => Err(x)
    }
}

/// Validate litecoin address checksum.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_ltc_address(addr: &str) -> Result<usize, ValidationError> {
    match validate_base58_hash(addr) {
        Ok(48) => Ok(48),      // real address
        Ok(111) => Ok(111),    // testnet address
        Ok(_) => Err(ValidationError::NotLitecoin),
        Err(x) => Err(x)
    }
}

#[test]
fn test_decoding() {
    use std::num::from_str_radix;

    assert_eq!(decode_base58("1"), from_int(0));
    assert_eq!(decode_base58("1AaZz"), from_int(0x1C8485));
    assert_eq!(decode_base58("AO"), None);
    assert_eq!(decode_base58("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"), from_str_radix("65A16059864A2FDBC7C99A4723A8395BC6F188EBC046B2FF", 16).ok());
}

#[test]
fn test_to_bytes() {
    assert_eq!(bigint_to_bytes(Zero::zero()), vec!(0));
    assert_eq!(bigint_to_bytes(from_int(1).unwrap()), vec!(1));
    assert_eq!(bigint_to_bytes(from_int(256).unwrap()), vec!(1, 0));
}

#[test]
fn test_validate_hash() {
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Ok(0));
    assert_eq!(validate_base58_hash("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"), Ok(111));
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYheX"), Err(ValidationError::HashMismatch));
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYh  "), Err(ValidationError::InvalidEncoding));
    assert_eq!(validate_base58_hash("1"), Err(ValidationError::HashMismatch));
    assert_eq!(validate_base58_hash(""), Err(ValidationError::TooShort));
}

#[test]
fn test_validate_btc_address() {
    assert_eq!(validate_btc_address("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Ok(0));
    assert_eq!(validate_btc_address("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"), Ok(5));
    assert_eq!(validate_btc_address("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"), Ok(111));
    assert_eq!(validate_btc_address("LRELGDJyeCPRDXz4Dh1kWorMN9hTBB7CEz"), Err(ValidationError::NotBitcoin));
}

#[test]
fn test_validate_ltc_address() {
    assert_eq!(validate_ltc_address("LRELGDJyeCPRDXz4Dh1kWorMN9hTBB7CEz"), Ok(48));
    assert_eq!(validate_ltc_address("muen9zszN6rVwXaFw48xh6YkdUSjJcfzek"), Ok(111));
    assert_eq!(validate_ltc_address("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Err(ValidationError::NotLitecoin));
}

#[bench]
fn bench_validate_hash(b: &mut test::Bencher) {
    b.iter(|| {
        validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem")
    })
}
<commit_msg>New traits for bigint conversion<commit_after>#![crate_type = "lib"]
#![crate_name = "coinaddress"]

//! Functions for validating the base58 hash checksums, including specifically
//! the bitcoin and litecoin addresses.

#![feature(test)]
#![feature(rustc_private)]

extern crate num;
extern crate rustc;
extern crate test;

use num::bigint::{ToBigUint,BigUint};
use num::traits::ToPrimitive;
use num::Zero;
use rustc::util::sha2::{Sha256, Digest};

#[derive(PartialEq, Debug)]
pub enum ValidationError {
    /// Given address is too short to be valid
    TooShort,
    /// Encoding is not a valid base58
    InvalidEncoding,
    /// Computed hash does not match the embedded one
    HashMismatch,

    // currency specific
    /// This address is not a bitcoin address (testnet or real).
    /// May happen when attempting to validate btc address
    NotBitcoin,
    /// This address is not a litecoin address.
    /// May happen when attempting to validate ltc address
    NotLitecoin,
}

static BASE58_CHARS: &'static str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

fn decode_base58(bc: &str) -> Option<BigUint> {
    let mut res: BigUint = Zero::zero();
    let b58: BigUint = 58.to_biguint().unwrap();

    for c in bc.chars() {
        match BASE58_CHARS.find(c) {
            None => {
                return None
            },
            Some(x) => {
                res = res * &b58 + x.to_biguint().unwrap();
            }
        }
    }
    Some(res)
}

fn bigint_to_bytes(n: BigUint) -> Vec<u8> {
    let mut res: Vec<u8> = Vec::new();
    let mut tmp = n;
    let b256: BigUint = 256.to_biguint().unwrap();

    while !tmp.is_zero() {
        res.insert(0, (&tmp % &b256).to_u8().unwrap());
        tmp = tmp / &b256;
    }

    if res.len() == 0 {
        return vec!(0);
    }

    res
}

fn pad_to(v: Vec<u8>, len: usize) -> Vec<u8> {
    let mut tmp: Vec<u8> = v;
    while tmp.len() < len {
        tmp.insert(0, 0)
    }
    tmp
}

fn double_sha256(chunk: &[u8]) -> Vec<u8> {
    let mut hash = Sha256::new();
    hash.input(chunk);
    let mut hash2 = Sha256::new();
    hash2.input(&hash.result_bytes()[..]);
    hash2.result_bytes()
}

/// Validate provided generic base58 hash.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_base58_hash(addr: &str) -> Result<usize, ValidationError> {
    if addr.len() == 0 {
        return Err(ValidationError::TooShort);
    }

    let big = match decode_base58(addr) {
        None => return Err(ValidationError::InvalidEncoding),
        Some(x) => x
    };
    let bytes = bigint_to_bytes(big);
    let padded = pad_to(bytes, 25);

    let hash = double_sha256(&padded[0 .. padded.len() - 4]);
    let short_hash = &hash[0..4];
    let known = &padded[padded.len()-4 .. padded.len()];
    if &short_hash[..] == known {
        Ok(padded[0] as usize)
    } else {
        Err(ValidationError::HashMismatch)
    }
}

/// Validate bitcoin address checksum.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_btc_address(addr: &str) -> Result<usize, ValidationError> {
    match validate_base58_hash(addr) {
        Ok(0) => Ok(0),      // real address
        Ok(5) => Ok(5),      // script hash
        Ok(111) => Ok(111),  // testnet address
        Ok(_) => Err(ValidationError::NotBitcoin),
        Err(x) => Err(x)
    }
}

/// Validate litecoin address checksum.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_ltc_address(addr: &str) -> Result<usize, ValidationError> {
    match validate_base58_hash(addr) {
        Ok(48) => Ok(48),      // real address
        Ok(111) => Ok(111),    // testnet address
        Ok(_) => Err(ValidationError::NotLitecoin),
        Err(x) => Err(x)
    }
}

#[test]
fn test_decoding() {
    use num::traits::Num;

    assert_eq!(decode_base58("1"), 0.to_biguint());
    assert_eq!(decode_base58("1AaZz"), 0x1C8485.to_biguint());
    assert_eq!(decode_base58("AO"), None);
    assert_eq!(decode_base58("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"), BigUint::from_str_radix("65A16059864A2FDBC7C99A4723A8395BC6F188EBC046B2FF", 16).ok());
}

#[test]
fn test_to_bytes() {
    assert_eq!(bigint_to_bytes(Zero::zero()), vec!(0));
    assert_eq!(bigint_to_bytes(1.to_biguint().unwrap()), vec!(1));
    assert_eq!(bigint_to_bytes(256.to_biguint().unwrap()), vec!(1, 0));
}

#[test]
fn test_validate_hash() {
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Ok(0));
    assert_eq!(validate_base58_hash("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"), Ok(111));
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYheX"), Err(ValidationError::HashMismatch));
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYh  "), Err(ValidationError::InvalidEncoding));
    assert_eq!(validate_base58_hash("1"), Err(ValidationError::HashMismatch));
    assert_eq!(validate_base58_hash(""), Err(ValidationError::TooShort));
}

#[test]
fn test_validate_btc_address() {
    assert_eq!(validate_btc_address("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Ok(0));
    assert_eq!(validate_btc_address("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"), Ok(5));
    assert_eq!(validate_btc_address("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"), Ok(111));
    assert_eq!(validate_btc_address("LRELGDJyeCPRDXz4Dh1kWorMN9hTBB7CEz"), Err(ValidationError::NotBitcoin));
}

#[test]
fn test_validate_ltc_address() {
    assert_eq!(validate_ltc_address("LRELGDJyeCPRDXz4Dh1kWorMN9hTBB7CEz"), Ok(48));
    assert_eq!(validate_ltc_address("muen9zszN6rVwXaFw48xh6YkdUSjJcfzek"), Ok(111));
    assert_eq!(validate_ltc_address("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Err(ValidationError::NotLitecoin));
}

#[bench]
fn bench_validate_hash(b: &mut test::Bencher) {
    b.iter(|| {
        validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem")
    })
}
<|endoftext|>


<commit_before>#![crate_type = "lib"]
#![crate_name = "coinaddress"]
#![license = "MIT"]

//! Functions for validating the base58 hash checksums, including specifically
//! the bitcoin and litecoin addresses.

extern crate num;
extern crate rustc;
extern crate test;

use num::bigint::BigUint;
use num::Zero;
use std::num::{from_uint, from_int};
use rustc::util::sha2::{Sha256, Digest};

#[deriving(PartialEq, Show)]
pub enum ValidationError {
    /// Given address is too short to be valid
    TooShort,
    /// Encoding is not a valid base58
    InvalidEncoding,
    /// Computed hash does not match the embedded one
    HashMismatch,

    // currency specific
    /// This address is not a bitcoin address (testnet or real).
    /// May happen when attempting to validate btc address
    NotBitcoin,
    /// This address is not a litecoin address.
    /// May happen when attempting to validate ltc address
    NotLitecoin,
}

static BASE58_CHARS: &'static str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

fn decode_base58(bc: &str) -> Option<BigUint> {
    let mut res: BigUint = Zero::zero();
    let b58: BigUint = from_int(58).unwrap();

    for c in bc.chars() {
        match BASE58_CHARS.find(c) {
            None => {
                return None
            },
            Some(x) => {
                res = res * b58 + from_uint(x).unwrap();
            }
        }
    }
    Some(res)
}

fn bigint_to_bytes(n: BigUint) -> Vec<u8> {
    let mut res: Vec<u8> = Vec::new();
    let mut tmp = n;
    let b256: BigUint = from_int(256).unwrap();

    while !tmp.is_zero() {
        res.insert(0, (tmp % b256).to_u8().unwrap());
        tmp = tmp / b256;
    }

    if res.len() == 0 {
        return vec!(0);
    }

    res
}

fn pad_to(v: Vec<u8>, len: uint) -> Vec<u8> {
    let mut tmp: Vec<u8> = v;
    while tmp.len() < len {
        tmp.insert(0, 0)
    }
    tmp
}

fn double_sha256(chunk: &[u8]) -> Vec<u8> {
    let mut hash = Sha256::new();
    hash.input(chunk);
    let mut hash2 = Sha256::new();
    hash2.input(hash.result_bytes().as_slice());
    hash2.result_bytes()
}

/// Validate provided generic base58 hash.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_base58_hash(addr: &str) -> Result<uint, ValidationError> {
    if addr.len() == 0 {
        return Err(ValidationError::TooShort);
    }

    let big = match decode_base58(addr) {
        None => return Err(ValidationError::InvalidEncoding),
        Some(x) => x
    };
    let bytes = bigint_to_bytes(big);
    let padded = pad_to(bytes, 25);

    let hash = double_sha256(padded.slice(0, padded.len() - 4));
    let short_hash = hash.slice(0, 4);
    let known = padded.slice(padded.len()-4, padded.len());
    if short_hash.as_slice() == known {
        Ok(padded[0].to_uint().unwrap())
    } else {
        Err(ValidationError::HashMismatch)
    }
}

/// Validate bitcoin address checksum.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_btc_address(addr: &str) -> Result<uint, ValidationError> {
    match validate_base58_hash(addr) {
        Ok(0) => Ok(0),      // real address
        Ok(5) => Ok(5),      // script hash
        Ok(111) => Ok(111),  // testnet address
        Ok(_) => Err(ValidationError::NotBitcoin),
        Err(x) => Err(x)
    }
}

/// Validate litecoin address checksum.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_ltc_address(addr: &str) -> Result<uint, ValidationError> {
    match validate_base58_hash(addr) {
        Ok(48) => Ok(48),      // real address
        Ok(111) => Ok(111),    // testnet address
        Ok(_) => Err(ValidationError::NotLitecoin),
        Err(x) => Err(x)
    }
}

#[test]
fn test_decoding() {
    use std::num::from_str_radix;

    assert_eq!(decode_base58("1"), from_int(0));
    assert_eq!(decode_base58("1AaZz"), from_int(0x1C8485));
    assert_eq!(decode_base58("AO"), None);
    assert_eq!(decode_base58("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"), from_str_radix("65A16059864A2FDBC7C99A4723A8395BC6F188EBC046B2FF", 16));
}

#[test]
fn test_to_bytes() {
    assert_eq!(bigint_to_bytes(Zero::zero()), vec!(0));
    assert_eq!(bigint_to_bytes(from_int(1).unwrap()), vec!(1));
    assert_eq!(bigint_to_bytes(from_int(256).unwrap()), vec!(1, 0));
}

#[test]
fn test_validate_hash() {
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Ok(0));
    assert_eq!(validate_base58_hash("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"), Ok(111));
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYheX"), Err(ValidationError::HashMismatch));
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYh  "), Err(ValidationError::InvalidEncoding));
    assert_eq!(validate_base58_hash("1"), Err(ValidationError::HashMismatch));
    assert_eq!(validate_base58_hash(""), Err(ValidationError::TooShort));
}

#[test]
fn test_validate_btc_address() {
    assert_eq!(validate_btc_address("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Ok(0));
    assert_eq!(validate_btc_address("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"), Ok(5));
    assert_eq!(validate_btc_address("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"), Ok(111));
    assert_eq!(validate_btc_address("LRELGDJyeCPRDXz4Dh1kWorMN9hTBB7CEz"), Err(ValidationError::NotBitcoin));
}

#[test]
fn test_validate_ltc_address() {
    assert_eq!(validate_ltc_address("LRELGDJyeCPRDXz4Dh1kWorMN9hTBB7CEz"), Ok(48));
    assert_eq!(validate_ltc_address("muen9zszN6rVwXaFw48xh6YkdUSjJcfzek"), Ok(111));
    assert_eq!(validate_ltc_address("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Err(ValidationError::NotLitecoin));
}

#[bench]
fn bench_validate_hash(b: &mut test::Bencher) {
    b.iter(|| {
        validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem")
    })
}
<commit_msg>Unused attribute removed<commit_after>#![crate_type = "lib"]
#![crate_name = "coinaddress"]

//! Functions for validating the base58 hash checksums, including specifically
//! the bitcoin and litecoin addresses.

extern crate num;
extern crate rustc;
extern crate test;

use num::bigint::BigUint;
use num::Zero;
use std::num::{from_uint, from_int};
use rustc::util::sha2::{Sha256, Digest};

#[deriving(PartialEq, Show)]
pub enum ValidationError {
    /// Given address is too short to be valid
    TooShort,
    /// Encoding is not a valid base58
    InvalidEncoding,
    /// Computed hash does not match the embedded one
    HashMismatch,

    // currency specific
    /// This address is not a bitcoin address (testnet or real).
    /// May happen when attempting to validate btc address
    NotBitcoin,
    /// This address is not a litecoin address.
    /// May happen when attempting to validate ltc address
    NotLitecoin,
}

static BASE58_CHARS: &'static str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

fn decode_base58(bc: &str) -> Option<BigUint> {
    let mut res: BigUint = Zero::zero();
    let b58: BigUint = from_int(58).unwrap();

    for c in bc.chars() {
        match BASE58_CHARS.find(c) {
            None => {
                return None
            },
            Some(x) => {
                res = res * b58 + from_uint(x).unwrap();
            }
        }
    }
    Some(res)
}

fn bigint_to_bytes(n: BigUint) -> Vec<u8> {
    let mut res: Vec<u8> = Vec::new();
    let mut tmp = n;
    let b256: BigUint = from_int(256).unwrap();

    while !tmp.is_zero() {
        res.insert(0, (tmp % b256).to_u8().unwrap());
        tmp = tmp / b256;
    }

    if res.len() == 0 {
        return vec!(0);
    }

    res
}

fn pad_to(v: Vec<u8>, len: uint) -> Vec<u8> {
    let mut tmp: Vec<u8> = v;
    while tmp.len() < len {
        tmp.insert(0, 0)
    }
    tmp
}

fn double_sha256(chunk: &[u8]) -> Vec<u8> {
    let mut hash = Sha256::new();
    hash.input(chunk);
    let mut hash2 = Sha256::new();
    hash2.input(hash.result_bytes().as_slice());
    hash2.result_bytes()
}

/// Validate provided generic base58 hash.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_base58_hash(addr: &str) -> Result<uint, ValidationError> {
    if addr.len() == 0 {
        return Err(ValidationError::TooShort);
    }

    let big = match decode_base58(addr) {
        None => return Err(ValidationError::InvalidEncoding),
        Some(x) => x
    };
    let bytes = bigint_to_bytes(big);
    let padded = pad_to(bytes, 25);

    let hash = double_sha256(padded.slice(0, padded.len() - 4));
    let short_hash = hash.slice(0, 4);
    let known = padded.slice(padded.len()-4, padded.len());
    if short_hash.as_slice() == known {
        Ok(padded[0].to_uint().unwrap())
    } else {
        Err(ValidationError::HashMismatch)
    }
}

/// Validate bitcoin address checksum.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_btc_address(addr: &str) -> Result<uint, ValidationError> {
    match validate_base58_hash(addr) {
        Ok(0) => Ok(0),      // real address
        Ok(5) => Ok(5),      // script hash
        Ok(111) => Ok(111),  // testnet address
        Ok(_) => Err(ValidationError::NotBitcoin),
        Err(x) => Err(x)
    }
}

/// Validate litecoin address checksum.
/// Returns the hash version/type if correct and an error otherwise.
pub fn validate_ltc_address(addr: &str) -> Result<uint, ValidationError> {
    match validate_base58_hash(addr) {
        Ok(48) => Ok(48),      // real address
        Ok(111) => Ok(111),    // testnet address
        Ok(_) => Err(ValidationError::NotLitecoin),
        Err(x) => Err(x)
    }
}

#[test]
fn test_decoding() {
    use std::num::from_str_radix;

    assert_eq!(decode_base58("1"), from_int(0));
    assert_eq!(decode_base58("1AaZz"), from_int(0x1C8485));
    assert_eq!(decode_base58("AO"), None);
    assert_eq!(decode_base58("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"), from_str_radix("65A16059864A2FDBC7C99A4723A8395BC6F188EBC046B2FF", 16));
}

#[test]
fn test_to_bytes() {
    assert_eq!(bigint_to_bytes(Zero::zero()), vec!(0));
    assert_eq!(bigint_to_bytes(from_int(1).unwrap()), vec!(1));
    assert_eq!(bigint_to_bytes(from_int(256).unwrap()), vec!(1, 0));
}

#[test]
fn test_validate_hash() {
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Ok(0));
    assert_eq!(validate_base58_hash("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"), Ok(111));
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYheX"), Err(ValidationError::HashMismatch));
    assert_eq!(validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYh  "), Err(ValidationError::InvalidEncoding));
    assert_eq!(validate_base58_hash("1"), Err(ValidationError::HashMismatch));
    assert_eq!(validate_base58_hash(""), Err(ValidationError::TooShort));
}

#[test]
fn test_validate_btc_address() {
    assert_eq!(validate_btc_address("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Ok(0));
    assert_eq!(validate_btc_address("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"), Ok(5));
    assert_eq!(validate_btc_address("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn"), Ok(111));
    assert_eq!(validate_btc_address("LRELGDJyeCPRDXz4Dh1kWorMN9hTBB7CEz"), Err(ValidationError::NotBitcoin));
}

#[test]
fn test_validate_ltc_address() {
    assert_eq!(validate_ltc_address("LRELGDJyeCPRDXz4Dh1kWorMN9hTBB7CEz"), Ok(48));
    assert_eq!(validate_ltc_address("muen9zszN6rVwXaFw48xh6YkdUSjJcfzek"), Ok(111));
    assert_eq!(validate_ltc_address("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"), Err(ValidationError::NotLitecoin));
}

#[bench]
fn bench_validate_hash(b: &mut test::Bencher) {
    b.iter(|| {
        validate_base58_hash("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem")
    })
}
<|endoftext|>


<commit_before>use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{Derive, Doc, ForeignName};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::parse::{Nothing, Parse, ParseStream, Parser as _};
use syn::{parenthesized, token, Attribute, Error, LitStr, Path, Result, Token};

// Intended usage:
//
//     let mut doc = Doc::new();
//     let mut cxx_name = None;
//     let mut rust_name = None;
//     /* ... */
//     let attrs = attrs::parse(
//         cx,
//         item.attrs,
//         attrs::Parser {
//             doc: Some(&mut doc),
//             cxx_name: Some(&mut cxx_name),
//             rust_name: Some(&mut rust_name),
//             /* ... */
//             ..Default::default()
//         },
//     );
//
#[derive(Default)]
pub struct Parser<'a> {
    pub doc: Option<&'a mut Doc>,
    pub derives: Option<&'a mut Vec<Derive>>,
    pub repr: Option<&'a mut Option<Atom>>,
    pub namespace: Option<&'a mut Namespace>,
    pub cxx_name: Option<&'a mut Option<ForeignName>>,
    pub rust_name: Option<&'a mut Option<Ident>>,
    pub variants_from_header: Option<&'a mut Option<Attribute>>,

    // Suppress clippy needless_update lint ("struct update has no effect, all
    // the fields in the struct have already been specified") when preemptively
    // writing `..Default::default()`.
    pub(crate) _more: (),
}

pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
    let mut passthrough_attrs = Vec::new();
    for attr in attrs {
        if attr.path.is_ident("doc") {
            match parse_doc_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(doc) = &mut parser.doc {
                        match attr {
                            DocAttribute::Doc(lit) => doc.push(lit),
                            DocAttribute::Hidden => doc.hidden = true,
                        }
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("derive") {
            match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
                Ok(attr) => {
                    if let Some(derives) = &mut parser.derives {
                        derives.extend(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("repr") {
            match attr.parse_args_with(parse_repr_attribute) {
                Ok(attr) => {
                    if let Some(repr) = &mut parser.repr {
                        **repr = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("namespace") {
            match parse_namespace_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(namespace) = &mut parser.namespace {
                        **namespace = attr;
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("cxx_name") {
            match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(cxx_name) = &mut parser.cxx_name {
                        **cxx_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("rust_name") {
            match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(rust_name) = &mut parser.rust_name {
                        **rust_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("variants_from_header") && cfg!(feature = "experimental") {
            if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) {
                cx.push(err);
            }
            if let Some(variants_from_header) = &mut parser.variants_from_header {
                **variants_from_header = Some(attr);
                continue;
            }
        } else if attr.path.is_ident("allow")
            || attr.path.is_ident("warn")
            || attr.path.is_ident("deny")
            || attr.path.is_ident("forbid")
            || attr.path.is_ident("deprecated")
            || attr.path.is_ident("must_use")
        {
            // https://doc.rust-lang.org/reference/attributes/diagnostics.html
            passthrough_attrs.push(attr);
            continue;
        } else if attr.path.segments.len() > 1 {
            let tool = &attr.path.segments.first().unwrap().ident;
            if tool == "rustfmt" {
                // Skip, rustfmt only needs to find it in the pre-expansion source file.
                continue;
            } else if tool == "clippy" {
                passthrough_attrs.push(attr);
                continue;
            }
        }
        cx.error(attr, "unsupported attribute");
        break;
    }
    OtherAttrs(passthrough_attrs)
}

enum DocAttribute {
    Doc(LitStr),
    Hidden,
}

mod kw {
    syn::custom_keyword!(hidden);
}

fn parse_doc_attribute(input: ParseStream) -> Result<DocAttribute> {
    let lookahead = input.lookahead1();
    if lookahead.peek(Token![=]) {
        input.parse::<Token![=]>()?;
        let lit: LitStr = input.parse()?;
        Ok(DocAttribute::Doc(lit))
    } else if lookahead.peek(token::Paren) {
        let content;
        parenthesized!(content in input);
        content.parse::<kw::hidden>()?;
        Ok(DocAttribute::Hidden)
    } else {
        Err(lookahead.error())
    }
}

fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
    let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;

    let mut derives = Vec::new();
    for path in paths {
        if let Some(ident) = path.get_ident() {
            if let Some(derive) = Derive::from(ident) {
                derives.push(derive);
                continue;
            }
        }
        cx.error(path, "unsupported derive");
    }
    Ok(derives)
}

fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
    let begin = input.cursor();
    let ident: Ident = input.parse()?;
    if let Some(atom) = Atom::from(&ident) {
        match atom {
            U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
                return Ok(atom);
            }
            _ => {}
        }
    }
    Err(Error::new_spanned(
        begin.token_stream(),
        "unrecognized repr",
    ))
}

fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
    input.parse::<Token![=]>()?;
    let namespace = input.parse::<Namespace>()?;
    Ok(namespace)
}

fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        ForeignName::parse(&lit.value(), lit.span())
    } else {
        let ident: Ident = input.parse()?;
        ForeignName::parse(&ident.to_string(), ident.span())
    }
}

fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        lit.parse()
    } else {
        input.parse()
    }
}

pub struct OtherAttrs(Vec<Attribute>);

impl OtherAttrs {
    pub fn none() -> Self {
        OtherAttrs(Vec::new())
    }
}

impl ToTokens for OtherAttrs {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for attr in &self.0 {
            let Attribute {
                pound_token,
                style,
                bracket_token,
                path,
                tokens: attr_tokens,
            } = attr;
            pound_token.to_tokens(tokens);
            let _ = style; // ignore; render outer and inner attrs both as outer
            bracket_token.surround(tokens, |tokens| {
                path.to_tokens(tokens);
                attr_tokens.to_tokens(tokens);
            });
        }
    }
}
<commit_msg>Pass through serde attributes<commit_after>use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{Derive, Doc, ForeignName};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::parse::{Nothing, Parse, ParseStream, Parser as _};
use syn::{parenthesized, token, Attribute, Error, LitStr, Path, Result, Token};

// Intended usage:
//
//     let mut doc = Doc::new();
//     let mut cxx_name = None;
//     let mut rust_name = None;
//     /* ... */
//     let attrs = attrs::parse(
//         cx,
//         item.attrs,
//         attrs::Parser {
//             doc: Some(&mut doc),
//             cxx_name: Some(&mut cxx_name),
//             rust_name: Some(&mut rust_name),
//             /* ... */
//             ..Default::default()
//         },
//     );
//
#[derive(Default)]
pub struct Parser<'a> {
    pub doc: Option<&'a mut Doc>,
    pub derives: Option<&'a mut Vec<Derive>>,
    pub repr: Option<&'a mut Option<Atom>>,
    pub namespace: Option<&'a mut Namespace>,
    pub cxx_name: Option<&'a mut Option<ForeignName>>,
    pub rust_name: Option<&'a mut Option<Ident>>,
    pub variants_from_header: Option<&'a mut Option<Attribute>>,

    // Suppress clippy needless_update lint ("struct update has no effect, all
    // the fields in the struct have already been specified") when preemptively
    // writing `..Default::default()`.
    pub(crate) _more: (),
}

pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
    let mut passthrough_attrs = Vec::new();
    for attr in attrs {
        if attr.path.is_ident("doc") {
            match parse_doc_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(doc) = &mut parser.doc {
                        match attr {
                            DocAttribute::Doc(lit) => doc.push(lit),
                            DocAttribute::Hidden => doc.hidden = true,
                        }
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("derive") {
            match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
                Ok(attr) => {
                    if let Some(derives) = &mut parser.derives {
                        derives.extend(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("repr") {
            match attr.parse_args_with(parse_repr_attribute) {
                Ok(attr) => {
                    if let Some(repr) = &mut parser.repr {
                        **repr = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("namespace") {
            match parse_namespace_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(namespace) = &mut parser.namespace {
                        **namespace = attr;
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("cxx_name") {
            match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(cxx_name) = &mut parser.cxx_name {
                        **cxx_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("rust_name") {
            match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(rust_name) = &mut parser.rust_name {
                        **rust_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("variants_from_header") && cfg!(feature = "experimental") {
            if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) {
                cx.push(err);
            }
            if let Some(variants_from_header) = &mut parser.variants_from_header {
                **variants_from_header = Some(attr);
                continue;
            }
        } else if attr.path.is_ident("allow")
            || attr.path.is_ident("warn")
            || attr.path.is_ident("deny")
            || attr.path.is_ident("forbid")
            || attr.path.is_ident("deprecated")
            || attr.path.is_ident("must_use")
        {
            // https://doc.rust-lang.org/reference/attributes/diagnostics.html
            passthrough_attrs.push(attr);
            continue;
        } else if attr.path.is_ident("serde") {
            passthrough_attrs.push(attr);
            continue;
        } else if attr.path.segments.len() > 1 {
            let tool = &attr.path.segments.first().unwrap().ident;
            if tool == "rustfmt" {
                // Skip, rustfmt only needs to find it in the pre-expansion source file.
                continue;
            } else if tool == "clippy" {
                passthrough_attrs.push(attr);
                continue;
            }
        }
        cx.error(attr, "unsupported attribute");
        break;
    }
    OtherAttrs(passthrough_attrs)
}

enum DocAttribute {
    Doc(LitStr),
    Hidden,
}

mod kw {
    syn::custom_keyword!(hidden);
}

fn parse_doc_attribute(input: ParseStream) -> Result<DocAttribute> {
    let lookahead = input.lookahead1();
    if lookahead.peek(Token![=]) {
        input.parse::<Token![=]>()?;
        let lit: LitStr = input.parse()?;
        Ok(DocAttribute::Doc(lit))
    } else if lookahead.peek(token::Paren) {
        let content;
        parenthesized!(content in input);
        content.parse::<kw::hidden>()?;
        Ok(DocAttribute::Hidden)
    } else {
        Err(lookahead.error())
    }
}

fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
    let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;

    let mut derives = Vec::new();
    for path in paths {
        if let Some(ident) = path.get_ident() {
            if let Some(derive) = Derive::from(ident) {
                derives.push(derive);
                continue;
            }
        }
        cx.error(path, "unsupported derive");
    }
    Ok(derives)
}

fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
    let begin = input.cursor();
    let ident: Ident = input.parse()?;
    if let Some(atom) = Atom::from(&ident) {
        match atom {
            U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
                return Ok(atom);
            }
            _ => {}
        }
    }
    Err(Error::new_spanned(
        begin.token_stream(),
        "unrecognized repr",
    ))
}

fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
    input.parse::<Token![=]>()?;
    let namespace = input.parse::<Namespace>()?;
    Ok(namespace)
}

fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        ForeignName::parse(&lit.value(), lit.span())
    } else {
        let ident: Ident = input.parse()?;
        ForeignName::parse(&ident.to_string(), ident.span())
    }
}

fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        lit.parse()
    } else {
        input.parse()
    }
}

pub struct OtherAttrs(Vec<Attribute>);

impl OtherAttrs {
    pub fn none() -> Self {
        OtherAttrs(Vec::new())
    }
}

impl ToTokens for OtherAttrs {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for attr in &self.0 {
            let Attribute {
                pound_token,
                style,
                bracket_token,
                path,
                tokens: attr_tokens,
            } = attr;
            pound_token.to_tokens(tokens);
            let _ = style; // ignore; render outer and inner attrs both as outer
            bracket_token.surround(tokens, |tokens| {
                path.to_tokens(tokens);
                attr_tokens.to_tokens(tokens);
            });
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;

fn main() {
    #[allow(unused_imports)]
    use glium::{glutin, Surface};

    let event_loop = glutin::event_loop::EventLoop::new();
    let wb = glutin::window::WindowBuilder::new();
    let cb = glutin::ContextBuilder::new();
    let display = glium::Display::new(wb, cb, &event_loop).unwrap();

    #[derive(Copy, Clone)]
    struct Vertex {
        position: [f32; 2],
    }

    implement_vertex!(Vertex, position);

    let vertex1 = Vertex { position: [-0.5, -0.5] };
    let vertex2 = Vertex { position: [ 0.0,  0.5] };
    let vertex3 = Vertex { position: [ 0.5, -0.25] };
    let shape = vec![vertex1, vertex2, vertex3];

    let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();
    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    let vertex_shader_src = r#"
        #version 140

        in vec2 position;
        uniform float t;

        void main() {
            vec2 pos = position;
            pos.x += t;
            gl_Position = vec4(pos, 0.0, 1.0);
        }
    "#;

    let fragment_shader_src = r#"
        #version 140

        out vec4 color;

        void main() {
            color = vec4(1.0, 0.0, 0.0, 1.0);
        }
    "#;

    let program = glium::Program::from_source(&display, vertex_shader_src, fragment_shader_src, None).unwrap();

    let mut t: f32 = -0.5;
    event_loop.run(move |event, _, control_flow| {
        let next_frame_time = std::time::Instant::now() +
            std::time::Duration::from_nanos(16_666_667);
        *control_flow = glutin::event_loop::ControlFlow::WaitUntil(next_frame_time);

        match event {
            glutin::event::Event::WindowEvent { event, .. } => match event {
                glutin::event::WindowEvent::CloseRequested => {
                    *control_flow = glutin::event_loop::ControlFlow::Exit;
                    return;
                },
                _ => return,
            },
            glutin::event::Event::NewEvents(cause) => match cause {
                glutin::event::StartCause::ResumeTimeReached { .. } => (),
                glutin::event::StartCause::Init => (),
                _ => return,
            },
            _ => return,
        }

        // we update `t`
        t += 0.0002;
        if t > 0.5 {
            t = -0.5;
        }

        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 1.0, 1.0);
        let uniforms = uniform! { t: t };
        target.draw(&vertex_buffer, &indices, &program, &uniforms,
                    &Default::default()).unwrap();
        target.finish().unwrap();
    });
}
<commit_msg>fixes #1799 You have to update the timers after the control flow. Then a new time is set only after you have passed the previous one<commit_after>#[macro_use]
extern crate glium;

fn main() {
    #[allow(unused_imports)]
    use glium::{glutin, Surface};

    let event_loop = glutin::event_loop::EventLoop::new();
    let wb = glutin::window::WindowBuilder::new();
    let cb = glutin::ContextBuilder::new();
    let display = glium::Display::new(wb, cb, &event_loop).unwrap();

    #[derive(Copy, Clone)]
    struct Vertex {
        position: [f32; 2],
    }

    implement_vertex!(Vertex, position);

    let vertex1 = Vertex { position: [-0.5, -0.5] };
    let vertex2 = Vertex { position: [ 0.0,  0.5] };
    let vertex3 = Vertex { position: [ 0.5, -0.25] };
    let shape = vec![vertex1, vertex2, vertex3];

    let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();
    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    let vertex_shader_src = r#"
        #version 140

        in vec2 position;
        uniform float t;

        void main() {
            vec2 pos = position;
            pos.x += t;
            gl_Position = vec4(pos, 0.0, 1.0);
        }
    "#;

    let fragment_shader_src = r#"
        #version 140

        out vec4 color;

        void main() {
            color = vec4(1.0, 0.0, 0.0, 1.0);
        }
    "#;

    let program = glium::Program::from_source(&display, vertex_shader_src, fragment_shader_src, None).unwrap();

    let mut t: f32 = -0.5;
    event_loop.run(move |event, _, control_flow| {

        match event {
            glutin::event::Event::WindowEvent { event, .. } => match event {
                glutin::event::WindowEvent::CloseRequested => {
                    *control_flow = glutin::event_loop::ControlFlow::Exit;
                    return;
                },
                _ => return,
            },
            glutin::event::Event::NewEvents(cause) => match cause {
                glutin::event::StartCause::ResumeTimeReached { .. } => (),
                glutin::event::StartCause::Init => (),
                _ => return,
            },
            _ => return,
        }

        let next_frame_time = std::time::Instant::now() +
            std::time::Duration::from_nanos(16_666_667);
        *control_flow = glutin::event_loop::ControlFlow::WaitUntil(next_frame_time);

        // we update `t`
        t += 0.0002;
        if t > 0.5 {
            t = -0.5;
        }

        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 1.0, 1.0);
        let uniforms = uniform! { t: t };
        target.draw(&vertex_buffer, &indices, &program, &uniforms,
                    &Default::default()).unwrap();
        target.finish().unwrap();
    });
}
<|endoftext|>