<commit_before>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

#![doc(html_logo_url = "https://raw.githubusercontent.com/Luz/hexdino/master/logo.png")]
#![deny(trivial_casts)]

use std::cmp;
use std::env;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::path::Path;

mod draw;
use draw::draw;
use draw::get_absolute_draw_indices;
mod find;
use find::FindOptSubset;

extern crate ncurses;
use ncurses::*;

extern crate getopts;
use getopts::Options;

extern crate pest;
#[macro_use]
extern crate pest_derive;

use pest::Parser;

#[derive(Parser)]
#[grammar = "cmd.pest"]
struct IdentParser;

extern crate memmem;
use memmem::{Searcher, TwoWaySearcher};

#[derive(PartialEq, Copy, Clone)]
pub enum Cursorstate {
    Leftnibble,
    Rightnibble,
    Asciichar,
}

fn main() {
    const VERSION: &str = env!("CARGO_PKG_VERSION");
    let mut buf = vec![];
    let mut cursorpos: usize = 0;
    let mut cstate: Cursorstate = Cursorstate::Leftnibble;
    // 0 = display data from first line of file
    let mut screenoffset: usize = 0;
    const SPALTEN: usize = 16;
    let mut command = String::new();
    let mut lastcommand = String::new();
    let mut debug = String::new();

    // start ncursesw
    initscr();
    let screenheight = getmaxy(stdscr()) as usize;
    // ctrl+z and fg works with this
    cbreak();
    noecho();
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);

    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("v", "version", "print the version");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            endwin();
            println!("{}", f.to_string());
            println!("Usage: {} FILE [options]", program);
            return;
        }
    };
    if matches.opt_present("v") {
        endwin();
        println!("Version: {}", VERSION);
        return;
    }
    if matches.opt_present("h") {
        endwin();
        println!("Usage: {} FILE [options]", program);
        return;
    }

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

    let patharg = match matches.free.is_empty() {
        true => String::new(),
        false => matches.free[0].clone(),
    };
    let path = Path::new(&patharg);

    if patharg.is_empty() {
        endwin();
        println!("Patharg is empty!\n");
        return;
    }

    let mut file = match OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(&path)
    {
        Err(why) => {
            endwin();
            println!("Could not open {}: {}", path.display(), why.to_string());
            return;
        }
        Ok(file) => file,
    };
    file.read_to_end(&mut buf)
        .ok()
        .expect("File could not be read.");

    let draw_range = get_absolute_draw_indices(buf.len(), SPALTEN, screenoffset);
    draw(
        &buf[draw_range.0..draw_range.1],
        cursorpos,
        SPALTEN,
        &command,
        &mut debug,
        cstate,
        screenoffset,
    );

    let mut quitnow = false;
    let mut autoparse = false;
    while quitnow == false {
        if !autoparse {
            let key = std::char::from_u32(getch() as u32).unwrap();
            command.push_str(&key.clone().to_string());
        }
        autoparse = false;

        let parsethisstring = command.clone();
        let commands = IdentParser::parse(Rule::cmd_list, &parsethisstring)
            .unwrap_or_else(|e| panic!("{}", e));

        let mut clear = true;
        let mut save = false;
        for cmd in commands {
            match cmd.as_rule() {
                Rule::down => {
                    // debug.push_str(&format!("{:?}", cmd.as_rule()));
                    if cursorpos + SPALTEN < buf.len() {
                        // not at end
                        cursorpos += SPALTEN;
                    } else {
                        // when at end
                        if buf.len() != 0 {
                            // Suppress underflow
                            cursorpos = buf.len() - 1;
                        }
                    }
                }
                Rule::up => {
                    if cursorpos >= SPALTEN {
                        cursorpos -= SPALTEN;
                    }
                }
                Rule::left => {
                    if cstate == Cursorstate::Asciichar {
                        if cursorpos > 0 {
                            cursorpos -= 1;
                        }
                    } else if cstate == Cursorstate::Rightnibble {
                        cstate = Cursorstate::Leftnibble;
                    } else if cstate == Cursorstate::Leftnibble {
                        if cursorpos > 0 {
                            // not at start
                            cstate = Cursorstate::Rightnibble;
                            cursorpos -= 1;
                        }
                    }
                }
                Rule::right => {
                    if cstate == Cursorstate::Asciichar {
                        if cursorpos + 1 < buf.len() {
                            // not at end
                            cursorpos += 1;
                        }
                    } else if cstate == Cursorstate::Leftnibble {
                        cstate = Cursorstate::Rightnibble;
                    } else if cstate == Cursorstate::Rightnibble {
                        if cursorpos + 1 < buf.len() {
                            // not at end
                            cstate = Cursorstate::Leftnibble;
                            cursorpos += 1;
                        }
                    }
                }
                Rule::start => {
                    cursorpos -= cursorpos % SPALTEN; // jump to start of line
                    if cstate == Cursorstate::Rightnibble {
                        cstate = Cursorstate::Leftnibble;
                    }
                }
                Rule::end => {
                    // check if no overflow
                    if cursorpos - (cursorpos % SPALTEN) + (SPALTEN - 1) < buf.len() {
                        // jump to end of line
                        cursorpos = cursorpos - (cursorpos % SPALTEN) + (SPALTEN - 1);
                    } else {
                        // jump to end of line
                        cursorpos = buf.len() - 1
                    }
                    if cstate == Cursorstate::Leftnibble {
                        cstate = Cursorstate::Rightnibble;
                    }
                }
                Rule::bottom => {
                    cursorpos = buf.len() - 1;
                    cursorpos -= cursorpos % SPALTEN; // jump to start of line
                }
                Rule::replace => {
                    // debug.push_str("next char will be the replacement!");
                    clear = false;
                }
                Rule::remove => {
                    // check if in valid range
                    if buf.len() > 0 && cursorpos < buf.len() {
                        // remove the current char
                        buf.remove(cursorpos);
                    }
                    // always perform the movement if possible
                    if cursorpos > 0 && cursorpos >= buf.len() {
                        cursorpos -= 1;
                    }
                    lastcommand = command.clone();
                }
                Rule::insert => {
                    // debug.push_str("next chars will be inserted!");
                    clear = false;
                }
                Rule::jumpascii => {
                    if cstate == Cursorstate::Asciichar {
                        cstate = Cursorstate::Leftnibble;
                    } else {
                        cstate = Cursorstate::Asciichar;
                    }
                }
                Rule::helpfile => {
                    command.push_str("No helpfile yet");
                }
                Rule::repeat => {
                    command = lastcommand.clone();
                    clear = false;
                    autoparse = true;
                }
                Rule::backspace => {
                    command.pop();
                    command.pop();
                    clear = false;
                }
                Rule::saveandexit => {
                    save = true;
                    quitnow = true;
                }
                Rule::exit => quitnow = true,
                Rule::save => save = true,

                _ => (),
            }

            for inner_cmd in cmd.into_inner() {
                match inner_cmd.as_rule() {
                    Rule::replacement => {
                        let key = inner_cmd.as_str().chars().nth(0).unwrap_or('x');
                        if cstate == Cursorstate::Asciichar {
                            if cursorpos >= buf.len() {
                                buf.insert(cursorpos, 0);
                            }
                            // buf[cursorpos] = inner_cmd.as_str();
                            buf[cursorpos] = key as u8;
                        } else {
                            let mask = if cstate == Cursorstate::Leftnibble {
                                0x0F
                            } else {
                                0xF0
                            };
                            let shift = if cstate == Cursorstate::Leftnibble {
                                4
                            } else {
                                0
                            };
                            if cursorpos >= buf.len() {
                                buf.insert(cursorpos, 0);
                            }
                            // Change the selected nibble
                            if let Some(c) = key.to_digit(16) {
                                buf[cursorpos] = buf[cursorpos] & mask | (c as u8) << shift;
                            }
                        }
                        lastcommand.clear();
                        lastcommand.push_str(&format!(
                            "{}{}",
                            command.chars().nth(0).unwrap_or('?'),
                            key
                        ));
                    }
                    Rule::dd_lines => {
                        let amount: usize = inner_cmd.as_str().parse().unwrap_or(1);
                        // check if in valid range
                        if buf.len() > 0 && cursorpos < buf.len() {
                            let startofline = cursorpos - cursorpos % SPALTEN;
                            let mut endofline =
                                cursorpos - (cursorpos % SPALTEN) + (SPALTEN * amount);
                            endofline = cmp::min(endofline, buf.len());
                            buf.drain(startofline..endofline);
                        }
                        lastcommand = command.clone();
                    }
                    Rule::insertment => {
                        let key = inner_cmd.as_str().chars().nth(0).unwrap_or('x');
                        lastcommand = command.clone();
                        // debug.push_str(&format!("Inserted: {:?}", inner_cmd.as_str()));
                        command.pop(); // remove the just inserted thing
                        clear = false;

                        if cstate == Cursorstate::Leftnibble {
                            // Left nibble
                            if let Some(c) = key.to_digit(16) {
                                buf.insert(cursorpos, (c as u8) << 4);
                                cstate = Cursorstate::Rightnibble;
                            }
                        } else if cstate == Cursorstate::Rightnibble {
                            // Right nibble
                            if cursorpos == buf.len() {
                                buf.insert(cursorpos, 0);
                            }
                            if let Some(c) = key.to_digit(16) {
                                buf[cursorpos] = buf[cursorpos] & 0xF0 | c as u8;
                                cstate = Cursorstate::Leftnibble;
                                cursorpos += 1;
                            }
                        } else if cstate == Cursorstate::Asciichar {
                            buf.insert(cursorpos, key as u8);
                            cursorpos += 1;
                        }
                        lastcommand.clear();
                        lastcommand.push_str(&format!(
                            "Command repeation for {:?} not yet implemented.",
                            inner_cmd.as_rule()
                        ));
                    }
                    Rule::searchstr => {
                        let search = inner_cmd.as_str().as_bytes();
                        let foundpos = TwoWaySearcher::new(&search);
                        cursorpos = foundpos.search_in(&buf).unwrap_or(cursorpos);
                    }
                    Rule::searchbytes => {
                        let search = inner_cmd.as_str().as_bytes();
                        let mut needle = vec![];
                        for i in 0..search.len() {
                            let nibble = match search[i] as u8 {
                                c @ 48..=57 => c - 48, // Numbers from 0 to 9
                                b'x' => 0x10,          // x is the wildcard
                                b'X' => 0x10,          // X is the wildcard
                                c @ b'a'..=b'f' => c - 87,
                                c @ b'A'..=b'F' => c - 55,
                                _ => panic!("Should not get to this position!"),
                            };
                            needle.push(nibble);
                        }
                        cursorpos = buf.find_subset(&needle).unwrap_or(cursorpos);
                        // debug.push_str(&format!("Searching for: {:?}", needle ));
                    }
                    Rule::gg_line => {
                        let linenr: usize = inner_cmd.as_str().parse().unwrap_or(0);
                        cursorpos = linenr * SPALTEN; // jump to the line
                        if cursorpos > buf.len() {
                            // detect file end
                            cursorpos = buf.len();
                        }
                        cursorpos -= cursorpos % SPALTEN; // jump to start of line
                    }
                    Rule::escape => (),
                    Rule::gatherone => clear = false,
                    _ => {
                        command.push_str(&format!("no rule for {:?} ", inner_cmd.as_rule()));
                        clear = false;
                    }
                };
            }
            if save {
                if path.exists() {
                    let mut file = match OpenOptions::new()
                        .read(true)
                        .write(true)
                        .create(true)
                        .open(&path)
                    {
                        Err(why) => {
                            panic!("Could not open {}: {}", path.display(), why.to_string())
                        }
                        Ok(file) => file,
                    };
                    file.seek(SeekFrom::Start(0))
                        .ok()
                        .expect("Filepointer could not be set to 0");
                    file.write_all(&mut buf)
                        .ok()
                        .expect("File could not be written.");
                    file.set_len(buf.len() as u64)
                        .ok()
                        .expect("File could not be set to correct lenght.");
                    command.push_str("File saved!");
                } else {
                    command.push_str("Careful, file could not be saved!");
                }
                // TODO: define filename during runtime
                save = false;
            }
            if clear {
                command.clear();
            }

            // Always move screen when cursor leaves screen
            if cursorpos > (screenheight + screenoffset - 1) * SPALTEN - 1 {
                screenoffset = 2 + cursorpos / SPALTEN - screenheight;
            }
            if cursorpos < screenoffset * SPALTEN {
                screenoffset = cursorpos / SPALTEN;
            }
        }

        let draw_range = get_absolute_draw_indices(buf.len(), SPALTEN, screenoffset);
        draw(
            &buf[draw_range.0..draw_range.1],
            cursorpos,
            SPALTEN,
            &command,
            &mut debug,
            cstate,
            screenoffset,
        );
    }

    refresh();
    endwin();
}
<commit_msg>Use default color for background<commit_after>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

#![doc(html_logo_url = "https://raw.githubusercontent.com/Luz/hexdino/master/logo.png")]
#![deny(trivial_casts)]

use std::cmp;
use std::env;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::path::Path;

mod draw;
use draw::draw;
use draw::get_absolute_draw_indices;
mod find;
use find::FindOptSubset;

extern crate ncurses;
use ncurses::*;

extern crate getopts;
use getopts::Options;

extern crate pest;
#[macro_use]
extern crate pest_derive;

use pest::Parser;

#[derive(Parser)]
#[grammar = "cmd.pest"]
struct IdentParser;

extern crate memmem;
use memmem::{Searcher, TwoWaySearcher};

#[derive(PartialEq, Copy, Clone)]
pub enum Cursorstate {
    Leftnibble,
    Rightnibble,
    Asciichar,
}

fn main() {
    const VERSION: &str = env!("CARGO_PKG_VERSION");
    let mut buf = vec![];
    let mut cursorpos: usize = 0;
    let mut cstate: Cursorstate = Cursorstate::Leftnibble;
    // 0 = display data from first line of file
    let mut screenoffset: usize = 0;
    const SPALTEN: usize = 16;
    let mut command = String::new();
    let mut lastcommand = String::new();
    let mut debug = String::new();

    // start ncursesw
    initscr();
    let screenheight = getmaxy(stdscr()) as usize;
    // ctrl+z and fg works with this
    cbreak();
    noecho();
    start_color();
    use_default_colors();
    init_pair(1, COLOR_GREEN, -1);

    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("v", "version", "print the version");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            endwin();
            println!("{}", f.to_string());
            println!("Usage: {} FILE [options]", program);
            return;
        }
    };
    if matches.opt_present("v") {
        endwin();
        println!("Version: {}", VERSION);
        return;
    }
    if matches.opt_present("h") {
        endwin();
        println!("Usage: {} FILE [options]", program);
        return;
    }

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

    let patharg = match matches.free.is_empty() {
        true => String::new(),
        false => matches.free[0].clone(),
    };
    let path = Path::new(&patharg);

    if patharg.is_empty() {
        endwin();
        println!("Patharg is empty!\n");
        return;
    }

    let mut file = match OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(&path)
    {
        Err(why) => {
            endwin();
            println!("Could not open {}: {}", path.display(), why.to_string());
            return;
        }
        Ok(file) => file,
    };
    file.read_to_end(&mut buf)
        .ok()
        .expect("File could not be read.");

    let draw_range = get_absolute_draw_indices(buf.len(), SPALTEN, screenoffset);
    draw(
        &buf[draw_range.0..draw_range.1],
        cursorpos,
        SPALTEN,
        &command,
        &mut debug,
        cstate,
        screenoffset,
    );

    let mut quitnow = false;
    let mut autoparse = false;
    while quitnow == false {
        if !autoparse {
            let key = std::char::from_u32(getch() as u32).unwrap();
            command.push_str(&key.clone().to_string());
        }
        autoparse = false;

        let parsethisstring = command.clone();
        let commands = IdentParser::parse(Rule::cmd_list, &parsethisstring)
            .unwrap_or_else(|e| panic!("{}", e));

        let mut clear = true;
        let mut save = false;
        for cmd in commands {
            match cmd.as_rule() {
                Rule::down => {
                    // debug.push_str(&format!("{:?}", cmd.as_rule()));
                    if cursorpos + SPALTEN < buf.len() {
                        // not at end
                        cursorpos += SPALTEN;
                    } else {
                        // when at end
                        if buf.len() != 0 {
                            // Suppress underflow
                            cursorpos = buf.len() - 1;
                        }
                    }
                }
                Rule::up => {
                    if cursorpos >= SPALTEN {
                        cursorpos -= SPALTEN;
                    }
                }
                Rule::left => {
                    if cstate == Cursorstate::Asciichar {
                        if cursorpos > 0 {
                            cursorpos -= 1;
                        }
                    } else if cstate == Cursorstate::Rightnibble {
                        cstate = Cursorstate::Leftnibble;
                    } else if cstate == Cursorstate::Leftnibble {
                        if cursorpos > 0 {
                            // not at start
                            cstate = Cursorstate::Rightnibble;
                            cursorpos -= 1;
                        }
                    }
                }
                Rule::right => {
                    if cstate == Cursorstate::Asciichar {
                        if cursorpos + 1 < buf.len() {
                            // not at end
                            cursorpos += 1;
                        }
                    } else if cstate == Cursorstate::Leftnibble {
                        cstate = Cursorstate::Rightnibble;
                    } else if cstate == Cursorstate::Rightnibble {
                        if cursorpos + 1 < buf.len() {
                            // not at end
                            cstate = Cursorstate::Leftnibble;
                            cursorpos += 1;
                        }
                    }
                }
                Rule::start => {
                    cursorpos -= cursorpos % SPALTEN; // jump to start of line
                    if cstate == Cursorstate::Rightnibble {
                        cstate = Cursorstate::Leftnibble;
                    }
                }
                Rule::end => {
                    // check if no overflow
                    if cursorpos - (cursorpos % SPALTEN) + (SPALTEN - 1) < buf.len() {
                        // jump to end of line
                        cursorpos = cursorpos - (cursorpos % SPALTEN) + (SPALTEN - 1);
                    } else {
                        // jump to end of line
                        cursorpos = buf.len() - 1
                    }
                    if cstate == Cursorstate::Leftnibble {
                        cstate = Cursorstate::Rightnibble;
                    }
                }
                Rule::bottom => {
                    cursorpos = buf.len() - 1;
                    cursorpos -= cursorpos % SPALTEN; // jump to start of line
                }
                Rule::replace => {
                    // debug.push_str("next char will be the replacement!");
                    clear = false;
                }
                Rule::remove => {
                    // check if in valid range
                    if buf.len() > 0 && cursorpos < buf.len() {
                        // remove the current char
                        buf.remove(cursorpos);
                    }
                    // always perform the movement if possible
                    if cursorpos > 0 && cursorpos >= buf.len() {
                        cursorpos -= 1;
                    }
                    lastcommand = command.clone();
                }
                Rule::insert => {
                    // debug.push_str("next chars will be inserted!");
                    clear = false;
                }
                Rule::jumpascii => {
                    if cstate == Cursorstate::Asciichar {
                        cstate = Cursorstate::Leftnibble;
                    } else {
                        cstate = Cursorstate::Asciichar;
                    }
                }
                Rule::helpfile => {
                    command.push_str("No helpfile yet");
                }
                Rule::repeat => {
                    command = lastcommand.clone();
                    clear = false;
                    autoparse = true;
                }
                Rule::backspace => {
                    command.pop();
                    command.pop();
                    clear = false;
                }
                Rule::saveandexit => {
                    save = true;
                    quitnow = true;
                }
                Rule::exit => quitnow = true,
                Rule::save => save = true,

                _ => (),
            }

            for inner_cmd in cmd.into_inner() {
                match inner_cmd.as_rule() {
                    Rule::replacement => {
                        let key = inner_cmd.as_str().chars().nth(0).unwrap_or('x');
                        if cstate == Cursorstate::Asciichar {
                            if cursorpos >= buf.len() {
                                buf.insert(cursorpos, 0);
                            }
                            // buf[cursorpos] = inner_cmd.as_str();
                            buf[cursorpos] = key as u8;
                        } else {
                            let mask = if cstate == Cursorstate::Leftnibble {
                                0x0F
                            } else {
                                0xF0
                            };
                            let shift = if cstate == Cursorstate::Leftnibble {
                                4
                            } else {
                                0
                            };
                            if cursorpos >= buf.len() {
                                buf.insert(cursorpos, 0);
                            }
                            // Change the selected nibble
                            if let Some(c) = key.to_digit(16) {
                                buf[cursorpos] = buf[cursorpos] & mask | (c as u8) << shift;
                            }
                        }
                        lastcommand.clear();
                        lastcommand.push_str(&format!(
                            "{}{}",
                            command.chars().nth(0).unwrap_or('?'),
                            key
                        ));
                    }
                    Rule::dd_lines => {
                        let amount: usize = inner_cmd.as_str().parse().unwrap_or(1);
                        // check if in valid range
                        if buf.len() > 0 && cursorpos < buf.len() {
                            let startofline = cursorpos - cursorpos % SPALTEN;
                            let mut endofline =
                                cursorpos - (cursorpos % SPALTEN) + (SPALTEN * amount);
                            endofline = cmp::min(endofline, buf.len());
                            buf.drain(startofline..endofline);
                        }
                        lastcommand = command.clone();
                    }
                    Rule::insertment => {
                        let key = inner_cmd.as_str().chars().nth(0).unwrap_or('x');
                        lastcommand = command.clone();
                        // debug.push_str(&format!("Inserted: {:?}", inner_cmd.as_str()));
                        command.pop(); // remove the just inserted thing
                        clear = false;

                        if cstate == Cursorstate::Leftnibble {
                            // Left nibble
                            if let Some(c) = key.to_digit(16) {
                                buf.insert(cursorpos, (c as u8) << 4);
                                cstate = Cursorstate::Rightnibble;
                            }
                        } else if cstate == Cursorstate::Rightnibble {
                            // Right nibble
                            if cursorpos == buf.len() {
                                buf.insert(cursorpos, 0);
                            }
                            if let Some(c) = key.to_digit(16) {
                                buf[cursorpos] = buf[cursorpos] & 0xF0 | c as u8;
                                cstate = Cursorstate::Leftnibble;
                                cursorpos += 1;
                            }
                        } else if cstate == Cursorstate::Asciichar {
                            buf.insert(cursorpos, key as u8);
                            cursorpos += 1;
                        }
                        lastcommand.clear();
                        lastcommand.push_str(&format!(
                            "Command repeation for {:?} not yet implemented.",
                            inner_cmd.as_rule()
                        ));
                    }
                    Rule::searchstr => {
                        let search = inner_cmd.as_str().as_bytes();
                        let foundpos = TwoWaySearcher::new(&search);
                        cursorpos = foundpos.search_in(&buf).unwrap_or(cursorpos);
                    }
                    Rule::searchbytes => {
                        let search = inner_cmd.as_str().as_bytes();
                        let mut needle = vec![];
                        for i in 0..search.len() {
                            let nibble = match search[i] as u8 {
                                c @ 48..=57 => c - 48, // Numbers from 0 to 9
                                b'x' => 0x10,          // x is the wildcard
                                b'X' => 0x10,          // X is the wildcard
                                c @ b'a'..=b'f' => c - 87,
                                c @ b'A'..=b'F' => c - 55,
                                _ => panic!("Should not get to this position!"),
                            };
                            needle.push(nibble);
                        }
                        cursorpos = buf.find_subset(&needle).unwrap_or(cursorpos);
                        // debug.push_str(&format!("Searching for: {:?}", needle ));
                    }
                    Rule::gg_line => {
                        let linenr: usize = inner_cmd.as_str().parse().unwrap_or(0);
                        cursorpos = linenr * SPALTEN; // jump to the line
                        if cursorpos > buf.len() {
                            // detect file end
                            cursorpos = buf.len();
                        }
                        cursorpos -= cursorpos % SPALTEN; // jump to start of line
                    }
                    Rule::escape => (),
                    Rule::gatherone => clear = false,
                    _ => {
                        command.push_str(&format!("no rule for {:?} ", inner_cmd.as_rule()));
                        clear = false;
                    }
                };
            }
            if save {
                if path.exists() {
                    let mut file = match OpenOptions::new()
                        .read(true)
                        .write(true)
                        .create(true)
                        .open(&path)
                    {
                        Err(why) => {
                            panic!("Could not open {}: {}", path.display(), why.to_string())
                        }
                        Ok(file) => file,
                    };
                    file.seek(SeekFrom::Start(0))
                        .ok()
                        .expect("Filepointer could not be set to 0");
                    file.write_all(&mut buf)
                        .ok()
                        .expect("File could not be written.");
                    file.set_len(buf.len() as u64)
                        .ok()
                        .expect("File could not be set to correct lenght.");
                    command.push_str("File saved!");
                } else {
                    command.push_str("Careful, file could not be saved!");
                }
                // TODO: define filename during runtime
                save = false;
            }
            if clear {
                command.clear();
            }

            // Always move screen when cursor leaves screen
            if cursorpos > (screenheight + screenoffset - 1) * SPALTEN - 1 {
                screenoffset = 2 + cursorpos / SPALTEN - screenheight;
            }
            if cursorpos < screenoffset * SPALTEN {
                screenoffset = cursorpos / SPALTEN;
            }
        }

        let draw_range = get_absolute_draw_indices(buf.len(), SPALTEN, screenoffset);
        draw(
            &buf[draw_range.0..draw_range.1],
            cursorpos,
            SPALTEN,
            &command,
            &mut debug,
            cstate,
            screenoffset,
        );
    }

    refresh();
    endwin();
}
<|endoftext|>


<commit_before>extern crate oko;
extern crate csv;
extern crate clap;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use oko::spot_price::SpotPriceApi;
use oko::spot_trading::SpotTradingApi;
use oko::TradeApi;
use std::fs::File;
use std::io::prelude::*;

fn main() {

    let mut wtr = csv::Writer::from_writer(std::io::stdout());

    // let t = SpotTradingApi::new(TradeApi::BTC,

    let matches = clap::App::new("OkCoinAPi")
        .version("1.0")
        .author("Eddy S.")
        .about("Does awesome things")
        .arg(
            clap::Arg::with_name("depth")
                .short("d")
                .long("depth")
                .help("get market depth")
                .takes_value(false)
                .requires("api"),
        )
        .arg(
            clap::Arg::with_name("ticker")
                .short("i")
                .long("ticker")
                .help("get market ticker")
                .takes_value(false)
                .requires("api"),
        )
        .arg(
            clap::Arg::with_name("trades")
                .short("r")
                .long("trades")
                .help("get market trades")
                .takes_value(false)
                .requires("api"),
        )
        .arg(
            clap::Arg::with_name("api")
                .short("a")
                .long("api")
                .value_name("api")
                .help("set the API to use (BTC/ETH/ETC/LTC)")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("keys")
                .short("k")
                .long("keys")
                .value_name("keys")
                .help("path to the keys.toml file")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("userinfo")
                .short("u")
                .long("userinfo")
                .help("get userinfo")
                .requires("keys"),
        )
        .arg(
            clap::Arg::with_name("trade_history")
                .short("y")
                .long("trade_history")
                .help("get trade history for a market")
                .value_name("since")
                .takes_value(true)
                .requires("keys")
                .requires("api"),
        )
        .get_matches();

    let api = match matches.value_of("api").unwrap_or("BTC") {
        "BTC" => TradeApi::BTC,
        "ETH" => TradeApi::ETH,
        "ETC" => TradeApi::ETC,
        "LTC" => TradeApi::LTC,
        _ => TradeApi::BTC,
    };

    let s = SpotPriceApi::new(api);

    if matches.is_present("depth") {
        let x = s.depth().unwrap();
        for i in x.depth {
            wtr.serialize(i).expect("Could not serialize Market Depth");
        }
    }
    if matches.is_present("ticker") {
        let x = s.ticker().unwrap();
        wtr.serialize(x.ticker).expect(
            "Could not serialize Market Ticker",
        );
    }
    if matches.is_present("trades") {
        let x = s.trades().unwrap();
        for i in x {
            wtr.serialize(i).expect("Could not serialize Market Trades");
        }
    }
    wtr.flush().expect("Could not flush stdout");

    if matches.is_present("keys") {
        let (apikey, secretkey) = {
            let mut file = File::open(matches.value_of("keys").unwrap_or("./key.toml"))
                .expect("could not read keyfile!");
            let mut keys = String::new();
            file.read_to_string(&mut keys).expect(
                "keys file not readable!",
            );
            let values = keys.parse::<toml::Value>().expect("keys not parsable!");
            (
                {
                    values["apiKey"]
                        .as_str()
                        .expect("no apiKey in file!")
                        .to_owned()
                },
                {
                    values["secretKey"]
                        .as_str()
                        .expect("no apiKey in file!")
                        .to_owned()
                },
            )
        };
        let s = SpotTradingApi::new(api, apikey, secretkey);

        if matches.is_present("userinfo") {
            println!("{:#?}", s.userinfo());
        }
        if matches.is_present("trade_history") {
            println!(
                "{:#?}",
                s.trade_history(
                    matches
                        .value_of("trade_history")
                        .expect("since date not provided!")
                        .parse()
                        .expect("since date is not parseable (use unix timestamps!)"),
                )
            );

        }
    }
}
<commit_msg>refactor api<commit_after>extern crate oko;
extern crate csv;
extern crate clap;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use oko::spot_price::SpotPriceApi;
use oko::spot_trading::SpotTradingApi;
use oko::TradeApi;
use std::fs::File;
use std::io::prelude::*;

fn main() {

    let mut wtr = csv::Writer::from_writer(std::io::stdout());

    // let t = SpotTradingApi::new(TradeApi::BTC,

    let matches = clap::App::new("OkCoinAPi")
        .version("1.0")
        .author("Eddy S.")
        .about("Does awesome things")

        // -------------------
        .arg(
            clap::Arg::with_name("api")
                .long("api")
                .value_name("api")
                .help("set the API to use (BTC/ETH/ETC/LTC)")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("keys")
                .long("keys")
                .value_name("keys")
                .help("path to the keys.toml file")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("price")
            .long("price")
            .value_name("price")
            .help("the price")
            .takes_value(true)
        )
        .arg(
            clap::Arg::with_name("amount")
            .long("amount")
            .value_name("amount")
            .help("the amount to trade")
            .takes_value(true)
        )
        .arg(
            clap::Arg::with_name("since")
            .long("since")
            .value_name("since")
            .help("date from when to start")
            .takes_value(true)
        )

        // --------------------
        .arg(
            clap::Arg::with_name("depth")
                .long("depth")
                .help("get market depth")
                .takes_value(false)
                .requires("api"),
        )
        .arg(
            clap::Arg::with_name("ticker")
                .long("ticker")
                .help("get markuet ticker")
                .takes_value(false)
                .requires("api"),
        )
        .arg(
            clap::Arg::with_name("trades")
                .long("trades")
                .help("get market trades")
                .takes_value(false)
                .requires("api"),
        )

        // -------------------
        .arg(
            clap::Arg::with_name("userinfo")
                .long("userinfo")
                .help("get userinfo")
                .requires("keys"),
        )

        // -------------------
        .arg(
            clap::Arg::with_name("trade_history")
                .long("trade_history")
                .help("get trade history for a market")
                .requires("since")
                .requires("keys")
                .requires("api"),
        )

        // ------------------
        .arg(
            clap::Arg::with_name("create_trade")
                .long("create_trade")
                .help("create a trade")
                .requires("api")
                .requires("price")
                .requires("amount")
                .requires("keys"),
        )

        .group(
            clap::ArgGroup::with_name("commands")
             .args(&["create_trade", "trade_history", "userinfo","trades", "ticker", "depth"])
             .required(true),
        )

        .get_matches();

    //-----------------------------------------------------------------------------------------------------

    let api = match matches.value_of("api").unwrap_or("BTC") {
        "BTC" => TradeApi::BTC,
        "ETH" => TradeApi::ETH,
        "ETC" => TradeApi::ETC,
        "LTC" => TradeApi::LTC,
        _ => TradeApi::BTC,
    };

    { // SPOT PRICE API
        let s = SpotPriceApi::new(api);

        if matches.is_present("depth") {
            let x = s.depth().unwrap();
            for i in x.depth {
                wtr.serialize(i).expect("Could not serialize Market Depth");
            }
        }
        if matches.is_present("ticker") {
            let x = s.ticker().unwrap();
            wtr.serialize(x.ticker).expect(
                "Could not serialize Market Ticker",
            );
        }
        if matches.is_present("trades") {
            let x = s.trades().unwrap();
            for i in x {
                wtr.serialize(i).expect("Could not serialize Market Trades");
            }
        }
    }

    { // PERSONAL API
        // ----------------------------

        let (apikey, secretkey) = {
            let mut file = File::open(matches.value_of("keys").unwrap_or("./key.toml"))
                .expect("could not read keyfile!");
            let mut keys = String::new();
            file.read_to_string(&mut keys).expect("keys file not readable!");
            let values = keys.parse::<toml::Value>().expect("keys not parsable!");

            ({
                values["apiKey"]
                    .as_str()
                    .expect("no apiKey in file!")
                    .to_owned()
             },{
                values["secretKey"]
                    .as_str()
                    .expect("no apiKey in file!")
                    .to_owned()
             })
        };
        let s = SpotTradingApi::new(api, apikey, secretkey);

        // ----------------
        if matches.is_present("userinfo") {
                println!("{:#?}", s.userinfo());
        }

        // -----------------
        if matches.is_present("trade_history") {
            println!(
                "{:#?}",
                s.trade_history(
                    matches
                        .value_of("since")
                        .expect("since date not provided!")
                        .parse()
                        .expect("since date is not parseable (use unix timestamps!)"),
                )
            );
        }

        //--------------------
        if matches.is_present("create_trade") {

        }

    }
    wtr.flush().expect("Could not flush stdout");

}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::sync::mpsc::{self, Sender, Receiver};
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::iter::IntoIterator;

use crossbeam::{Scope, ScopedJoinHandle};

struct Packet<T> {
    // this should be unique for a given instance of `*ParMap`
    idx: usize,
    data: Option<T>,
}

impl<T> PartialOrd for Packet<T> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}
impl<T> Ord for Packet<T> {
    // reverse the ordering, to work with the max-heap
    fn cmp(&self, other: &Self) -> Ordering { other.idx.cmp(&self.idx) }
}
impl<T> PartialEq for Packet<T> {
    fn eq(&self, other: &Self) -> bool { self.idx == other.idx }
}
impl<T> Eq for Packet<T> {}

/// A parallel-mapping iterator that doesn't care about the order in
/// which elements come out.
pub struct UnorderedParMap<T: Send> {
    rx: Receiver<Packet<T>>,
    _guards: Vec<ScopedJoinHandle<()>>
}

impl<T: 'static + Send> Iterator for UnorderedParMap<T> {
    type Item = (usize, T);

    fn next(&mut self) -> Option<(usize, T)> {
        match self.rx.recv() {
            Ok(Packet { data: Some(x), idx }) => Some((idx, x)),
            Ok(Packet { data: None, .. }) => {
                panic!("simple_parallel::unordered_map: closure panicked")
            }
            Err(mpsc::RecvError) => None,
        }
    }
}

struct Panicker<T: Send + 'static> {
    tx: Sender<Packet<T>>,
    idx: usize,
    all_ok: bool
}
impl<T: Send + 'static> Drop for Panicker<T> {
    fn drop(&mut self) {
        if !self.all_ok {
            let _ = self.tx.send(Packet { idx: self.idx, data: None });
        }
    }
}

/// Execute `f` on each element in `iter`, with unspecified yield order.
///
/// This behaves like `simple_parallel::map`, but does not make
/// efforts to ensure that the elements are returned in the order of
/// `iter`, hence this is cheaper.
pub fn unordered_map<'a, I: IntoIterator, F, T>(scope: &Scope<'a>, iter: I, f: F) -> UnorderedParMap<T>
    where I::Item: Send + 'a,
          F: 'a + Send + Sync + Fn(I::Item) -> T,
          T: Send + 'static
{
    let (tx, rx) = mpsc::channel();
    let f = Arc::new(f);
    let guards = iter.into_iter().enumerate().map(|(idx, elem)| {
        let tx = tx.clone();
        let f = f.clone();

        scope.spawn(move || {
            let mut p = Panicker { tx: tx, idx: idx, all_ok: false };
            let val = f(elem);
            let _ = p.tx.send(Packet { idx: idx, data: Some(val) });
            p.all_ok = true;
        })
    }).collect();

    UnorderedParMap {
        rx: rx,
        _guards: guards,
    }
}

/// A parallel-mapping iterator.
pub struct ParMap<T:  Send> {
    unordered: UnorderedParMap<T>,
    looking_for: usize,
    queue: BinaryHeap<Packet<T>>
}

impl<T: Send + 'static> Iterator for ParMap<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        loop {
            if self.queue.peek().map_or(false, |x| x.idx == self.looking_for) {
                // we've found what we want, so lets return it

                let packet = self.queue.pop().unwrap();
                self.looking_for += 1;
                match packet.data {
                    Some(x) => return Some(x),
                    None => panic!("simple_parallel::map: closure panicked")
                }
            }
            match self.unordered.rx.recv() {
                // this could be optimised to check for `packet.idx ==
                // self.looking_for` to avoid the BinaryHeap
                // interaction if its what we want.
                Ok(packet) => self.queue.push(packet),
                // all done
                Err(mpsc::RecvError) => return None,
            }
        }
    }
}

/// Execute `f` on `iter`, yielding the results in the order the
/// correspond to in `iter`.
///
/// This is a drop-in replacement for `iter.map(f)`, that runs in
/// parallel, and eagerly consumes `iter` spawning a thread for each
/// element.
pub fn map<'a, I: IntoIterator, F, T>(scope: &Scope<'a>, iter: I, f: F) -> ParMap<T>
    where I::Item: 'a + Send,
          F: 'a + Send + Sync + Fn(I::Item) -> T,
          T: Send + 'static
{
    ParMap {
        unordered: unordered_map(scope, iter, f),
        looking_for: 0,
        queue: BinaryHeap::new(),
    }
}
<commit_msg>Remove unnecessary `'static` bound on map functions.<commit_after>use std::sync::Arc;
use std::sync::mpsc::{self, Sender, Receiver};
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::iter::IntoIterator;

use crossbeam::{Scope, ScopedJoinHandle};

struct Packet<T> {
    // this should be unique for a given instance of `*ParMap`
    idx: usize,
    data: Option<T>,
}

impl<T> PartialOrd for Packet<T> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}
impl<T> Ord for Packet<T> {
    // reverse the ordering, to work with the max-heap
    fn cmp(&self, other: &Self) -> Ordering { other.idx.cmp(&self.idx) }
}
impl<T> PartialEq for Packet<T> {
    fn eq(&self, other: &Self) -> bool { self.idx == other.idx }
}
impl<T> Eq for Packet<T> {}

/// A parallel-mapping iterator that doesn't care about the order in
/// which elements come out.
pub struct UnorderedParMap<T: Send> {
    rx: Receiver<Packet<T>>,
    _guards: Vec<ScopedJoinHandle<()>>
}

impl<T: Send> Iterator for UnorderedParMap<T> {
    type Item = (usize, T);

    fn next(&mut self) -> Option<(usize, T)> {
        match self.rx.recv() {
            Ok(Packet { data: Some(x), idx }) => Some((idx, x)),
            Ok(Packet { data: None, .. }) => {
                panic!("simple_parallel::unordered_map: closure panicked")
            }
            Err(mpsc::RecvError) => None,
        }
    }
}

struct Panicker<T: Send> {
    tx: Sender<Packet<T>>,
    idx: usize,
    all_ok: bool
}
impl<T: Send> Drop for Panicker<T> {
    fn drop(&mut self) {
        if !self.all_ok {
            let _ = self.tx.send(Packet { idx: self.idx, data: None });
        }
    }
}

/// Execute `f` on each element in `iter`, with unspecified yield order.
///
/// This behaves like `simple_parallel::map`, but does not make
/// efforts to ensure that the elements are returned in the order of
/// `iter`, hence this is cheaper.
pub fn unordered_map<'a, I: IntoIterator, F, T>(scope: &Scope<'a>, iter: I, f: F) -> UnorderedParMap<T>
    where I::Item: Send + 'a,
          F: 'a + Send + Sync + Fn(I::Item) -> T,
          T: Send + 'a
{
    let (tx, rx) = mpsc::channel();
    let f = Arc::new(f);
    let guards = iter.into_iter().enumerate().map(|(idx, elem)| {
        let tx = tx.clone();
        let f = f.clone();

        scope.spawn(move || {
            let mut p = Panicker { tx: tx, idx: idx, all_ok: false };
            let val = f(elem);
            let _ = p.tx.send(Packet { idx: idx, data: Some(val) });
            p.all_ok = true;
        })
    }).collect();

    UnorderedParMap {
        rx: rx,
        _guards: guards,
    }
}

/// A parallel-mapping iterator.
pub struct ParMap<T:  Send> {
    unordered: UnorderedParMap<T>,
    looking_for: usize,
    queue: BinaryHeap<Packet<T>>
}

impl<T: Send> Iterator for ParMap<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        loop {
            if self.queue.peek().map_or(false, |x| x.idx == self.looking_for) {
                // we've found what we want, so lets return it

                let packet = self.queue.pop().unwrap();
                self.looking_for += 1;
                match packet.data {
                    Some(x) => return Some(x),
                    None => panic!("simple_parallel::map: closure panicked")
                }
            }
            match self.unordered.rx.recv() {
                // this could be optimised to check for `packet.idx ==
                // self.looking_for` to avoid the BinaryHeap
                // interaction if its what we want.
                Ok(packet) => self.queue.push(packet),
                // all done
                Err(mpsc::RecvError) => return None,
            }
        }
    }
}

/// Execute `f` on `iter`, yielding the results in the order the
/// correspond to in `iter`.
///
/// This is a drop-in replacement for `iter.map(f)`, that runs in
/// parallel, and eagerly consumes `iter` spawning a thread for each
/// element.
pub fn map<'a, I: IntoIterator, F, T>(scope: &Scope<'a>, iter: I, f: F) -> ParMap<T>
    where I::Item: 'a + Send,
          F: 'a + Send + Sync + Fn(I::Item) -> T,
          T: Send + 'a
{
    ParMap {
        unordered: unordered_map(scope, iter, f),
        looking_for: 0,
        queue: BinaryHeap::new(),
    }
}
<|endoftext|>


<commit_before>use property::Property;
use handle::*;
use connectivity::*;

pub struct Mesh {
    n_v_ : usize,

    vconn_ : Property<Vertex,VertexConnectivity>,
    hconn_ : Property<Halfedge,HalfedgeConnectivity>,
    fconn_ : Property<Face,FaceConnectivity>,
}

impl Mesh {
    /// Constructs an empty `Mesh`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::mesh::Mesh;
    ///
    /// let m = Mesh::new();
    /// ```
    pub fn new() -> Mesh {
        Mesh {
            n_v_ : 0,
            vconn_ : Property::new(),
            hconn_ : Property::new(),
            fconn_ : Property::new(),
        }
    }

    /// Adds a new vertex to the `Mesh`
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::mesh::Mesh;
    ///
    /// let mut m = Mesh::new();
    /// let v = m.add_vertex();
    /// ```
    pub fn add_vertex(&mut self) -> Vertex {
        let vc = VertexConnectivity {
                halfedge_ : Halfedge::invalid()
        };
        self.n_v_+=1;
        self.vconn_.push(vc)
    }

    /// Returns the number of vertices in the `Mesh`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::mesh::Mesh;
    ///
    /// let mut m = Mesh::new();
    /// let v1 = m.add_vertex();
    /// let v2 = m.add_vertex();
    /// assert!(m.n_vertices() == 2);
    /// ```
    pub fn n_vertices(& self) -> usize {
        self.n_v_
    }
}

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

    #[test]
    fn add_vertex() {
        let mut m = Mesh::new();
        assert!(m.n_v_ == 0);

        let v0 = m.add_vertex();
        assert!(m.n_v_ == 1);
        assert!(v0.idx().unwrap() == 0);

        m.add_vertex();
        let v2 = m.add_vertex();
        assert!(m.n_v_ == 3);
        assert!(v2.idx().unwrap() == 2);
    }

    #[test]
    fn n_vertices() {
        let mut m = Mesh::new();
        assert!(m.n_vertices() == 0);

        m.add_vertex();
        assert!(m.n_vertices() == 1);

        m.add_vertex();
        m.add_vertex();
        assert!(m.n_vertices() == 3);
    }
}
<commit_msg>mesh : adds access method<commit_after>use property::Property;
use handle::*;
use connectivity::*;

pub struct Mesh {
    n_v_ : usize,

    vconn_ : Property<Vertex,VertexConnectivity>,
    hconn_ : Property<Halfedge,HalfedgeConnectivity>,
    fconn_ : Property<Face,FaceConnectivity>,
}

impl Mesh {
    /// Constructs an empty `Mesh`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::mesh::Mesh;
    ///
    /// let m = Mesh::new();
    /// ```
    pub fn new() -> Mesh {
        Mesh {
            n_v_ : 0,
            vconn_ : Property::new(),
            hconn_ : Property::new(),
            fconn_ : Property::new(),
        }
    }

    /// Adds a new vertex to the `Mesh`
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::mesh::Mesh;
    ///
    /// let mut m = Mesh::new();
    /// let v = m.add_vertex();
    /// ```
    pub fn add_vertex(&mut self) -> Vertex {
        let vc = VertexConnectivity {
                halfedge_ : Halfedge::invalid()
        };
        self.n_v_+=1;
        self.vconn_.push(vc)
    }

    /// Returns the number of vertices in the `Mesh`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::mesh::Mesh;
    ///
    /// let mut m = Mesh::new();
    /// let v1 = m.add_vertex();
    /// let v2 = m.add_vertex();
    /// assert!(m.n_vertices() == 2);
    /// ```
    pub fn n_vertices(& self) -> usize {
        self.n_v_
    }

    /// Returns if the `Vertex` is on a boundary
    ///
    /// # Examples
    ///
    /// ```
    /// use lwmesh::mesh::Mesh;
    ///
    /// let mut m = Mesh::new();
    /// let v = m.add_vertex();
    /// assert!(m.is_boundary_vertex(v));
    /// ```
    pub fn is_boundary_vertex(&self, v : Vertex) -> bool {
        let h = self.halfedge(v);
        !(h.is_valid() && self.face(h).is_valid())
    }

    /// Returns if the `Halfedge` is on a boundary
    pub fn is_boundary_halfedge(&self, h : Halfedge) -> bool {
        !(h.is_valid() && self.face(h).is_valid())
    }

    /// Returns the `Face` incident to the `Halfedge`.
    pub fn face(&self, h : Halfedge) -> Face {
        self.hconn_[h].face_
    }

    /// Returns an outgoing `Haldedge` of `Vertex` `v`.
    pub fn halfedge(&self, v : Vertex) -> Halfedge {
        self.vconn_[v].halfedge_
    }

    /// Returns the `Vertex` the `Halfedge` h points to.
    pub fn to_vertex(&self, h : Halfedge) -> Vertex {
        self.hconn_[h].vertex_
    }

    /// Returns the `Vertex` the `Halfedge` h emanates from.
    pub fn from_vertex(&self, h : Halfedge) -> Vertex {
        self.hconn_[self.hconn_[h].prev_halfedge_].vertex_
    }

    /// Returns the next `Halfedge` within the incident face.
    pub fn next_halfedge(&self, h : Halfedge) -> Halfedge {
        self.hconn_[h].next_halfedge_
    }

    /// Returns the previous `Halfedge` within the incident face.
    pub fn prev_halfedge(&self, h : Halfedge) -> Halfedge {
        self.hconn_[h].prev_halfedge_
    }

    /// Returns the opposite `Halfedge` of h.
    pub fn opposite_halfedge(&self, h : Halfedge) -> Halfedge {
        let idx = h.idx().unwrap();
        if (idx & 1) == 1 {
            Halfedge::new(idx-1)
        } else {
            Halfedge::new(idx+1)
        }
    }

    /// Retunrs the `Halfedge` that is rotated clockwise around the start `Vertex` of h.
    pub fn cw_rotated_halfedge(&self, h : Halfedge) -> Halfedge {
        self.next_halfedge(self.opposite_halfedge(h))
    }

    /// find the `Halfedge` from start to end.
    pub fn find_halfedge(&self, start : Vertex, end : Vertex) -> Halfedge {
        if start.is_valid() && end.is_valid() {
            let mut h = self.halfedge(start);
            let h_end = h;
            if h.is_valid() {
                loop {
                    if self.to_vertex(h) == end {return h;}
                    h = self.cw_rotated_halfedge(h);
                    if h == h_end {break;}
                }
            }
        }
        Halfedge::invalid()
    }
}

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

    #[test]
    fn add_vertex() {
        let mut m = Mesh::new();
        assert!(m.n_v_ == 0);

        let v0 = m.add_vertex();
        assert!(m.n_v_ == 1);
        assert!(v0.idx().unwrap() == 0);

        m.add_vertex();
        let v2 = m.add_vertex();
        assert!(m.n_v_ == 3);
        assert!(v2.idx().unwrap() == 2);
    }

    #[test]
    fn n_vertices() {
        let mut m = Mesh::new();
        assert!(m.n_vertices() == 0);

        m.add_vertex();
        assert!(m.n_vertices() == 1);

        m.add_vertex();
        m.add_vertex();
        assert!(m.n_vertices() == 3);
    }
}
<|endoftext|>


<commit_before>use num::Num;
use set::identity;

#[inline(always)]
pub fn inverse<T: Num>(out: &mut [T; 16], a: [T; 16]) -> &mut [T; 16] {
    let a00 = a[0];
    let a01 = a[1];
    let a02 = a[2];
    let a03 = a[3];
    let a10 = a[4];
    let a11 = a[5];
    let a12 = a[6];
    let a13 = a[7];
    let a20 = a[8];
    let a21 = a[9];
    let a22 = a[10];
    let a23 = a[11];
    let a30 = a[12];
    let a31 = a[13];
    let a32 = a[14];
    let a33 = a[15];

    let b00 = a00 * a11 - a01 * a10;
    let b01 = a00 * a12 - a02 * a10;
    let b02 = a00 * a13 - a03 * a10;
    let b03 = a01 * a12 - a02 * a11;
    let b04 = a01 * a13 - a03 * a11;
    let b05 = a02 * a13 - a03 * a12;
    let b06 = a20 * a31 - a21 * a30;
    let b07 = a20 * a32 - a22 * a30;
    let b08 = a20 * a33 - a23 * a30;
    let b09 = a21 * a32 - a22 * a31;
    let b10 = a21 * a33 - a23 * a31;
    let b11 = a22 * a33 - a23 * a32;

    let d = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;

    if d != T::zero() {
        let inv_d = T::one() / d;

        out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * inv_d;
        out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * inv_d;
        out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * inv_d;
        out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * inv_d;
        out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * inv_d;
        out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * inv_d;
        out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * inv_d;
        out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * inv_d;
        out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * inv_d;
        out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * inv_d;
        out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * inv_d;
        out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * inv_d;
        out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * inv_d;
        out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * inv_d;
        out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * inv_d;
        out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * inv_d;
        out
    } else {
        identity(out)
    }
}
#[test]
fn test_inverse() {
    let mut v = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
    inverse(&mut v, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
    assert!(v == [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
}

#[inline(always)]
pub fn determinant<T: Num>(out: [T; 16]) -> T {
    let a00 = out[0];
    let a01 = out[1];
    let a02 = out[2];
    let a03 = out[3];
    let a10 = out[4];
    let a11 = out[5];
    let a12 = out[6];
    let a13 = out[7];
    let a20 = out[8];
    let a21 = out[9];
    let a22 = out[10];
    let a23 = out[11];
    let a30 = out[12];
    let a31 = out[13];
    let a32 = out[14];
    let a33 = out[15];

    let b00 = a00 * a11 - a01 * a10;
    let b01 = a00 * a12 - a02 * a10;
    let b02 = a00 * a13 - a03 * a10;
    let b03 = a01 * a12 - a02 * a11;
    let b04 = a01 * a13 - a03 * a11;
    let b05 = a02 * a13 - a03 * a12;
    let b06 = a20 * a31 - a21 * a30;
    let b07 = a20 * a32 - a22 * a30;
    let b08 = a20 * a33 - a23 * a30;
    let b09 = a21 * a32 - a22 * a31;
    let b10 = a21 * a33 - a23 * a31;
    let b11 = a22 * a33 - a23 * a32;

    b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
}
#[test]
fn test_determinant() {
    assert_eq!(determinant([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), 1);
}

#[inline(always)]
pub fn transpose<T: Num>(out: &mut [T; 16], a: [T; 16]) -> &mut [T; 16] {
    if *out == a {
        let a01 = a[1];
        let a02 = a[2];
        let a03 = a[3];
        let a12 = a[6];
        let a13 = a[7];
        let a23 = a[11];

        out[1] = a[4];
        out[2] = a[8];
        out[3] = a[12];
        out[4] = a01;
        out[6] = a[9];
        out[7] = a[13];
        out[8] = a02;
        out[9] = a12;
        out[11] = a[14];
        out[12] = a03;
        out[13] = a13;
        out[14] = a23;
    } else {
        out[0] = a[0];
        out[1] = a[4];
        out[2] = a[8];
        out[3] = a[12];
        out[4] = a[1];
        out[5] = a[5];
        out[6] = a[9];
        out[7] = a[13];
        out[8] = a[2];
        out[9] = a[6];
        out[10] = a[10];
        out[11] = a[14];
        out[12] = a[3];
        out[13] = a[7];
        out[14] = a[11];
        out[15] = a[15];
    }
    out
}
#[test]
fn test_transpose() {
    let mut v = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
    transpose(&mut v, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
    assert_eq!(v, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
}
<commit_msg>updates<commit_after>use num::Num;
use set::identity;

#[inline(always)]
pub fn inverse<T: Num>(out: &mut [T; 16], a: [T; 16]) -> &mut [T; 16] {
    let a00 = a[0];
    let a01 = a[1];
    let a02 = a[2];
    let a03 = a[3];
    let a10 = a[4];
    let a11 = a[5];
    let a12 = a[6];
    let a13 = a[7];
    let a20 = a[8];
    let a21 = a[9];
    let a22 = a[10];
    let a23 = a[11];
    let a30 = a[12];
    let a31 = a[13];
    let a32 = a[14];
    let a33 = a[15];

    let b00 = a00 * a11 - a01 * a10;
    let b01 = a00 * a12 - a02 * a10;
    let b02 = a00 * a13 - a03 * a10;
    let b03 = a01 * a12 - a02 * a11;
    let b04 = a01 * a13 - a03 * a11;
    let b05 = a02 * a13 - a03 * a12;
    let b06 = a20 * a31 - a21 * a30;
    let b07 = a20 * a32 - a22 * a30;
    let b08 = a20 * a33 - a23 * a30;
    let b09 = a21 * a32 - a22 * a31;
    let b10 = a21 * a33 - a23 * a31;
    let b11 = a22 * a33 - a23 * a32;

    let d = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;

    if d != T::zero() {
        let inv_d = T::one() / d;

        out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * inv_d;
        out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * inv_d;
        out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * inv_d;
        out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * inv_d;
        out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * inv_d;
        out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * inv_d;
        out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * inv_d;
        out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * inv_d;
        out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * inv_d;
        out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * inv_d;
        out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * inv_d;
        out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * inv_d;
        out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * inv_d;
        out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * inv_d;
        out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * inv_d;
        out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * inv_d;
        out
    } else {
        identity(out)
    }
}
#[test]
fn test_inverse() {
    let mut v = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
    inverse(&mut v, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
    assert!(v == [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
}

#[inline(always)]
pub fn determinant<T: Num>(out: [T; 16]) -> T {
    let a00 = out[0];
    let a01 = out[1];
    let a02 = out[2];
    let a03 = out[3];
    let a10 = out[4];
    let a11 = out[5];
    let a12 = out[6];
    let a13 = out[7];
    let a20 = out[8];
    let a21 = out[9];
    let a22 = out[10];
    let a23 = out[11];
    let a30 = out[12];
    let a31 = out[13];
    let a32 = out[14];
    let a33 = out[15];

    let b00 = a00 * a11 - a01 * a10;
    let b01 = a00 * a12 - a02 * a10;
    let b02 = a00 * a13 - a03 * a10;
    let b03 = a01 * a12 - a02 * a11;
    let b04 = a01 * a13 - a03 * a11;
    let b05 = a02 * a13 - a03 * a12;
    let b06 = a20 * a31 - a21 * a30;
    let b07 = a20 * a32 - a22 * a30;
    let b08 = a20 * a33 - a23 * a30;
    let b09 = a21 * a32 - a22 * a31;
    let b10 = a21 * a33 - a23 * a31;
    let b11 = a22 * a33 - a23 * a32;

    b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
}
#[test]
fn test_determinant() {
    assert_eq!(determinant([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), 1);
}

#[inline(always)]
pub fn transpose<T: Num>(out: &mut [T; 16], a: [T; 16]) -> &mut [T; 16] {
    out[0] = a[0];
    out[1] = a[4];
    out[2] = a[8];
    out[3] = a[12];
    out[4] = a[1];
    out[5] = a[5];
    out[6] = a[9];
    out[7] = a[13];
    out[8] = a[2];
    out[9] = a[6];
    out[10] = a[10];
    out[11] = a[14];
    out[12] = a[3];
    out[13] = a[7];
    out[14] = a[11];
    out[15] = a[15];
    out
}
#[test]
fn test_transpose() {
    let mut v = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
    transpose(&mut v, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
    assert_eq!(v, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
}
<|endoftext|>


<commit_before>use std::io;
use std::io::{IoResult, IoError};
use std::iter::range_step_inclusive;
use time::Tm;
use types;
use util;

static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
static DATA_DESCRIPTOR_SIGNATURE : u32 = 0x08074b50;
static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
static CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50;

#[deriving(Clone)]
pub struct LocalFileHeader
{
    pub extract_version: u16,

    // general purpose flags
    pub encrypted: bool, // bit 0
    // bit 1 & 2 unused
    pub has_descriptor: bool, // bit 3
    // bit 4 unused
    pub is_compressed_patch: bool, // bit 5
    pub strong_encryption: bool, // bit 6
    // bit 7 - 10 unused
    pub is_utf8: bool, // bit 11
    // bit 12 unused
    pub is_masked: bool, // bit 13
    // bit 14 & 15 unused

    pub compression_method: types::CompressionMethod,
    pub last_modified: Tm,
    pub crc32: u32,
    pub compressed_size: u32,
    pub uncompressed_size: u32,
    pub file_name: Vec<u8>,
    pub extra_field: Vec<u8>,
    pub header_end: u64,
}

impl LocalFileHeader
{
    pub fn parse<T: Reader+Seek>(reader: &mut T) -> IoResult<LocalFileHeader>
    {
        let signature = try!(reader.read_le_u32());
        if signature != LOCAL_FILE_HEADER_SIGNATURE
        {
            return Err(IoError {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Invalid local file header",
                detail: None })
        }
        let version = try!(reader.read_le_u16());
        let flags = try!(reader.read_le_u16());
        let compression_method = try!(reader.read_le_u16());
        let last_mod_time = try!(reader.read_le_u16());
        let last_mod_date = try!(reader.read_le_u16());
        let crc = try!(reader.read_le_u32());
        let compressed_size = try!(reader.read_le_u32());
        let uncompressed_size = try!(reader.read_le_u32());
        let file_name_length = try!(reader.read_le_u16());
        let extra_field_length = try!(reader.read_le_u16());
        let file_name = try!(reader.read_exact(file_name_length as uint));
        let extra_field = try!(reader.read_exact(extra_field_length as uint));
        let header_end = try!(reader.tell());

        Ok(LocalFileHeader
           {
               extract_version: version,
               encrypted: (flags & (1 << 0)) != 0,
               has_descriptor: (flags & (1 << 3)) != 0,
               is_compressed_patch: (flags & (1 << 5)) != 0,
               strong_encryption: (flags & (1 << 6)) != 0,
               is_utf8: (flags & (1 << 11)) != 0,
               is_masked: (flags & (1 << 13)) != 0,
               compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(types::Unknown),
               last_modified: util::msdos_datetime_to_tm(last_mod_time, last_mod_date),
               crc32: crc,
               compressed_size: compressed_size,
               uncompressed_size: uncompressed_size,
               file_name: file_name,
               extra_field: extra_field,
               header_end: header_end,
           })
    }
}

#[deriving(Clone)]
pub struct DataDescriptor
{
    pub compressed_size: u32,
    pub uncompressed_size: u32,
    pub crc32: u32,
}

impl DataDescriptor
{
    pub fn parse<T: Reader>(reader: &mut T) -> IoResult<DataDescriptor>
    {
        let first = try!(reader.read_le_u32());
        let compressed = if first == DATA_DESCRIPTOR_SIGNATURE
        {
            try!(reader.read_le_u32())
        }
        else
        {
            first
        };

        let uncompressed = try!(reader.read_le_u32());
        let crc = try!(reader.read_le_u32());

        Ok(DataDescriptor
           {
               compressed_size: compressed,
               uncompressed_size: uncompressed,
               crc32: crc,
           })
    }
}

#[deriving(Clone)]
pub struct CentralDirectoryHeader
{
    pub made_by: u16,
    pub version_needed: u16,

    // general purpose flags
    pub encrypted: bool, // bit 0
    // bit 1 & 2 unused
    pub has_descriptor: bool, // bit 3
    // bit 4 unused
    pub is_compressed_patch: bool, // bit 5
    pub strong_encryption: bool, // bit 6
    // bit 7 - 10 unused
    pub is_utf8: bool, // bit 11
    // bit 12 unused
    pub is_masked: bool, // bit 13
    // bit 14 & 15 unused

    pub compression_method: types::CompressionMethod,
    pub last_modified_time: Tm,
    pub crc32: u32,
    pub compressed_size: u32,
    pub uncompressed_size: u32,
    pub file_name: Vec<u8>,
    pub extra_field: Vec<u8>,
    pub file_comment: Vec<u8>,
    pub disk_number: u16,
    pub file_offset: u32,
}

impl CentralDirectoryHeader
{
    pub fn parse<T: Reader>(reader: &mut T) -> IoResult<CentralDirectoryHeader>
    {
        let signature = try!(reader.read_le_u32());
        if signature != CENTRAL_DIRECTORY_HEADER_SIGNATURE
        {
            return Err(IoError {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Invalid central directory header",
                detail: None })
        }

        let made_by = try!(reader.read_le_u16());
        let version_needed = try!(reader.read_le_u16());
        let flags = try!(reader.read_le_u16());
        let compression = try!(reader.read_le_u16());
        let last_mod_time = try!(reader.read_le_u16());
        let last_mod_date = try!(reader.read_le_u16());
        let crc = try!(reader.read_le_u32());
        let compressed_size = try!(reader.read_le_u32());
        let uncompressed_size = try!(reader.read_le_u32());
        let file_name_length = try!(reader.read_le_u16()) as uint;
        let extra_field_length = try!(reader.read_le_u16()) as uint;
        let file_comment_length = try!(reader.read_le_u16()) as uint;
        let disk_number = try!(reader.read_le_u16());
        try!(reader.read_le_u16()); // internal file attribute
        try!(reader.read_le_u32()); // external file attribute
        let offset = try!(reader.read_le_u32());
        let file_name = try!(reader.read_exact(file_name_length));
        let extra_field = try!(reader.read_exact(extra_field_length));
        let file_comment  = try!(reader.read_exact(file_comment_length));

        Ok(CentralDirectoryHeader
           {
               made_by: made_by,
               version_needed: version_needed,
               encrypted: flags & (1 << 0) != 0,
               has_descriptor: flags & (1 << 3) != 0,
               is_compressed_patch: flags & (1 << 5) != 0,
               strong_encryption: flags & (1 << 6) != 0,
               is_utf8: flags & (1 << 11) != 0,
               is_masked: flags & (1 << 13) != 0,
               compression_method: FromPrimitive::from_u16(compression).unwrap_or(types::Unknown),
               last_modified_time: util::msdos_datetime_to_tm(last_mod_time, last_mod_date),
               crc32: crc,
               compressed_size: compressed_size,
               uncompressed_size: uncompressed_size,
               file_name: file_name,
               extra_field: extra_field,
               file_comment: file_comment,
               disk_number: disk_number,
               file_offset: offset,
            })
    }
}

#[deriving(Show)]
pub struct CentralDirectoryEnd
{
    pub number_of_disks: u16,
    pub disk_with_central_directory: u16,
    pub number_of_files_on_this_disk: u16,
    pub number_of_files: u16,
    pub central_directory_size: u32,
    pub central_directory_offset: u32,
    pub zip_file_comment: Vec<u8>,
}

impl CentralDirectoryEnd
{
    pub fn parse<T: Reader>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
    {
        let magic = try!(reader.read_le_u32());
        if magic != CENTRAL_DIRECTORY_END_SIGNATURE
        {
            return Err(IoError {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Invalid digital signature header",
                detail: None })
        }
        let number_of_disks = try!(reader.read_le_u16());
        let disk_with_central_directory = try!(reader.read_le_u16());
        let number_of_files_on_this_disk = try!(reader.read_le_u16());
        let number_of_files = try!(reader.read_le_u16());
        let central_directory_size = try!(reader.read_le_u32());
        let central_directory_offset = try!(reader.read_le_u32());
        let zip_file_comment_length = try!(reader.read_le_u16()) as uint;
        let zip_file_comment = try!(reader.read_exact(zip_file_comment_length));

        Ok(CentralDirectoryEnd
           {
               number_of_disks: number_of_disks,
               disk_with_central_directory: disk_with_central_directory,
               number_of_files_on_this_disk: number_of_files_on_this_disk,
               number_of_files: number_of_files,
               central_directory_size: central_directory_size,
               central_directory_offset: central_directory_offset,
               zip_file_comment: zip_file_comment,
           })
    }
    pub fn find_and_parse<T: Reader+Seek>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
    {
        let header_size = 22;
        let bytes_between_magic_and_comment_size = header_size - 6;
        try!(reader.seek(0, io::SeekEnd));
        let file_length = try!(reader.tell()) as i64;

        let search_upper_bound = ::std::cmp::max(0, file_length - header_size - ::std::u16::MAX as i64);
        for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1)
        {
            try!(reader.seek(pos, io::SeekSet));
            if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE
            {
                try!(reader.seek(bytes_between_magic_and_comment_size, io::SeekCur));
                let comment_length = try!(reader.read_le_u16()) as i64;
                if file_length - pos - header_size == comment_length
                {
                    try!(reader.seek(pos, io::SeekSet));
                    return CentralDirectoryEnd::parse(reader);
                }
            }
        }
        Err(IoError
            {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Could not find central directory end",
                detail: None
            })
    }
}
<commit_msg>Remove Data Descriptor<commit_after>use std::io;
use std::io::{IoResult, IoError};
use std::iter::range_step_inclusive;
use time::Tm;
use types;
use util;

static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
static CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50;

pub struct LocalFileHeader
{
    pub extract_version: u16,

    // general purpose flags
    pub encrypted: bool, // bit 0
    // bit 1 & 2 unused
    pub has_descriptor: bool, // bit 3
    // bit 4 unused
    pub is_compressed_patch: bool, // bit 5
    pub strong_encryption: bool, // bit 6
    // bit 7 - 10 unused
    pub is_utf8: bool, // bit 11
    // bit 12 unused
    pub is_masked: bool, // bit 13
    // bit 14 & 15 unused

    pub compression_method: types::CompressionMethod,
    pub last_modified: Tm,
    pub crc32: u32,
    pub compressed_size: u32,
    pub uncompressed_size: u32,
    pub file_name: Vec<u8>,
    pub extra_field: Vec<u8>,
    pub header_end: u64,
}

impl LocalFileHeader
{
    pub fn parse<T: Reader+Seek>(reader: &mut T) -> IoResult<LocalFileHeader>
    {
        let signature = try!(reader.read_le_u32());
        if signature != LOCAL_FILE_HEADER_SIGNATURE
        {
            return Err(IoError {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Invalid local file header",
                detail: None })
        }
        let version = try!(reader.read_le_u16());
        let flags = try!(reader.read_le_u16());
        let compression_method = try!(reader.read_le_u16());
        let last_mod_time = try!(reader.read_le_u16());
        let last_mod_date = try!(reader.read_le_u16());
        let crc = try!(reader.read_le_u32());
        let compressed_size = try!(reader.read_le_u32());
        let uncompressed_size = try!(reader.read_le_u32());
        let file_name_length = try!(reader.read_le_u16());
        let extra_field_length = try!(reader.read_le_u16());
        let file_name = try!(reader.read_exact(file_name_length as uint));
        let extra_field = try!(reader.read_exact(extra_field_length as uint));
        let header_end = try!(reader.tell());

        Ok(LocalFileHeader
           {
               extract_version: version,
               encrypted: (flags & (1 << 0)) != 0,
               has_descriptor: (flags & (1 << 3)) != 0,
               is_compressed_patch: (flags & (1 << 5)) != 0,
               strong_encryption: (flags & (1 << 6)) != 0,
               is_utf8: (flags & (1 << 11)) != 0,
               is_masked: (flags & (1 << 13)) != 0,
               compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(types::Unknown),
               last_modified: util::msdos_datetime_to_tm(last_mod_time, last_mod_date),
               crc32: crc,
               compressed_size: compressed_size,
               uncompressed_size: uncompressed_size,
               file_name: file_name,
               extra_field: extra_field,
               header_end: header_end,
           })
    }
}

pub struct CentralDirectoryHeader
{
    pub made_by: u16,
    pub version_needed: u16,

    // general purpose flags
    pub encrypted: bool, // bit 0
    // bit 1 & 2 unused
    pub has_descriptor: bool, // bit 3
    // bit 4 unused
    pub is_compressed_patch: bool, // bit 5
    pub strong_encryption: bool, // bit 6
    // bit 7 - 10 unused
    pub is_utf8: bool, // bit 11
    // bit 12 unused
    pub is_masked: bool, // bit 13
    // bit 14 & 15 unused

    pub compression_method: types::CompressionMethod,
    pub last_modified_time: Tm,
    pub crc32: u32,
    pub compressed_size: u32,
    pub uncompressed_size: u32,
    pub file_name: Vec<u8>,
    pub extra_field: Vec<u8>,
    pub file_comment: Vec<u8>,
    pub disk_number: u16,
    pub file_offset: u32,
}

impl CentralDirectoryHeader
{
    pub fn parse<T: Reader>(reader: &mut T) -> IoResult<CentralDirectoryHeader>
    {
        let signature = try!(reader.read_le_u32());
        if signature != CENTRAL_DIRECTORY_HEADER_SIGNATURE
        {
            return Err(IoError {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Invalid central directory header",
                detail: None })
        }

        let made_by = try!(reader.read_le_u16());
        let version_needed = try!(reader.read_le_u16());
        let flags = try!(reader.read_le_u16());
        let compression = try!(reader.read_le_u16());
        let last_mod_time = try!(reader.read_le_u16());
        let last_mod_date = try!(reader.read_le_u16());
        let crc = try!(reader.read_le_u32());
        let compressed_size = try!(reader.read_le_u32());
        let uncompressed_size = try!(reader.read_le_u32());
        let file_name_length = try!(reader.read_le_u16()) as uint;
        let extra_field_length = try!(reader.read_le_u16()) as uint;
        let file_comment_length = try!(reader.read_le_u16()) as uint;
        let disk_number = try!(reader.read_le_u16());
        try!(reader.read_le_u16()); // internal file attribute
        try!(reader.read_le_u32()); // external file attribute
        let offset = try!(reader.read_le_u32());
        let file_name = try!(reader.read_exact(file_name_length));
        let extra_field = try!(reader.read_exact(extra_field_length));
        let file_comment  = try!(reader.read_exact(file_comment_length));

        Ok(CentralDirectoryHeader
           {
               made_by: made_by,
               version_needed: version_needed,
               encrypted: flags & (1 << 0) != 0,
               has_descriptor: flags & (1 << 3) != 0,
               is_compressed_patch: flags & (1 << 5) != 0,
               strong_encryption: flags & (1 << 6) != 0,
               is_utf8: flags & (1 << 11) != 0,
               is_masked: flags & (1 << 13) != 0,
               compression_method: FromPrimitive::from_u16(compression).unwrap_or(types::Unknown),
               last_modified_time: util::msdos_datetime_to_tm(last_mod_time, last_mod_date),
               crc32: crc,
               compressed_size: compressed_size,
               uncompressed_size: uncompressed_size,
               file_name: file_name,
               extra_field: extra_field,
               file_comment: file_comment,
               disk_number: disk_number,
               file_offset: offset,
            })
    }
}

pub struct CentralDirectoryEnd
{
    pub number_of_disks: u16,
    pub disk_with_central_directory: u16,
    pub number_of_files_on_this_disk: u16,
    pub number_of_files: u16,
    pub central_directory_size: u32,
    pub central_directory_offset: u32,
    pub zip_file_comment: Vec<u8>,
}

impl CentralDirectoryEnd
{
    pub fn parse<T: Reader>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
    {
        let magic = try!(reader.read_le_u32());
        if magic != CENTRAL_DIRECTORY_END_SIGNATURE
        {
            return Err(IoError {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Invalid digital signature header",
                detail: None })
        }
        let number_of_disks = try!(reader.read_le_u16());
        let disk_with_central_directory = try!(reader.read_le_u16());
        let number_of_files_on_this_disk = try!(reader.read_le_u16());
        let number_of_files = try!(reader.read_le_u16());
        let central_directory_size = try!(reader.read_le_u32());
        let central_directory_offset = try!(reader.read_le_u32());
        let zip_file_comment_length = try!(reader.read_le_u16()) as uint;
        let zip_file_comment = try!(reader.read_exact(zip_file_comment_length));

        Ok(CentralDirectoryEnd
           {
               number_of_disks: number_of_disks,
               disk_with_central_directory: disk_with_central_directory,
               number_of_files_on_this_disk: number_of_files_on_this_disk,
               number_of_files: number_of_files,
               central_directory_size: central_directory_size,
               central_directory_offset: central_directory_offset,
               zip_file_comment: zip_file_comment,
           })
    }
    pub fn find_and_parse<T: Reader+Seek>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
    {
        let header_size = 22;
        let bytes_between_magic_and_comment_size = header_size - 6;
        try!(reader.seek(0, io::SeekEnd));
        let file_length = try!(reader.tell()) as i64;

        let search_upper_bound = ::std::cmp::max(0, file_length - header_size - ::std::u16::MAX as i64);
        for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1)
        {
            try!(reader.seek(pos, io::SeekSet));
            if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE
            {
                try!(reader.seek(bytes_between_magic_and_comment_size, io::SeekCur));
                let comment_length = try!(reader.read_le_u16()) as i64;
                if file_length - pos - header_size == comment_length
                {
                    try!(reader.seek(pos, io::SeekSet));
                    return CentralDirectoryEnd::parse(reader);
                }
            }
        }
        Err(IoError
            {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Could not find central directory end",
                detail: None
            })
    }
}
<|endoftext|>