<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 CursorSelects {
    LeftNibble,
    RightNibble,
    AsciiChar,
}

#[derive(Copy, Clone)]
pub struct CursorState {
    pos: usize,
    sel: CursorSelects,
}

fn main() {
    const VERSION: &str = env!("CARGO_PKG_VERSION");
    let mut buf = vec![];
    let mut cursor = CursorState {
        pos: 0,
        sel: CursorSelects::LeftNibble,
    };
    // 0 = display data from first line of file
    let mut screenoffset: usize = 0;
    const COLS: 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(), COLS, screenoffset);
    draw(
        &buf[draw_range.0..draw_range.1],
        COLS,
        &command,
        &mut debug,
        cursor,
        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 cursor.pos + COLS < buf.len() {
                        // not at end
                        cursor.pos += COLS;
                    } else {
                        // when at end
                        if buf.len() != 0 {
                            // Suppress underflow
                            cursor.pos = buf.len() - 1;
                        }
                    }
                }
                Rule::up => {
                    if cursor.pos >= COLS {
                        cursor.pos -= COLS;
                    }
                }
                Rule::left => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos > 0 {
                            cursor.pos -= 1;
                        }
                    } else if cursor.sel == CursorSelects::RightNibble {
                        cursor.sel = CursorSelects::LeftNibble;
                    } else if cursor.sel == CursorSelects::LeftNibble {
                        if cursor.pos > 0 {
                            // not at start
                            cursor.sel = CursorSelects::RightNibble;
                            cursor.pos -= 1;
                        }
                    }
                }
                Rule::right => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos + 1 < buf.len() {
                            // not at end
                            cursor.pos += 1;
                        }
                    } else if cursor.sel == CursorSelects::LeftNibble {
                        cursor.sel = CursorSelects::RightNibble;
                    } else if cursor.sel == CursorSelects::RightNibble {
                        if cursor.pos + 1 < buf.len() {
                            // not at end
                            cursor.sel = CursorSelects::LeftNibble;
                            cursor.pos += 1;
                        }
                    }
                }
                Rule::start => {
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                    if cursor.sel == CursorSelects::RightNibble {
                        cursor.sel = CursorSelects::LeftNibble;
                    }
                }
                Rule::end => {
                    // check if no overflow
                    if cursor.pos - (cursor.pos % COLS) + (COLS - 1) < buf.len() {
                        // jump to end of line
                        cursor.pos = cursor.pos - (cursor.pos % COLS) + (COLS - 1);
                    } else {
                        // jump to end of line
                        cursor.pos = buf.len() - 1
                    }
                    if cursor.sel == CursorSelects::LeftNibble {
                        cursor.sel = CursorSelects::RightNibble;
                    }
                }
                Rule::bottom => {
                    cursor.pos = buf.len() - 1;
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                }
                Rule::replace => {
                    clear = false;
                }
                Rule::replacement => {
                    let key = command.chars().last().unwrap_or('x');
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos >= buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        buf[cursor.pos] = key as u8;
                    } else {
                        let mask = if cursor.sel == CursorSelects::LeftNibble {
                            0x0F
                        } else {
                            0xF0
                        };
                        let shift = if cursor.sel == CursorSelects::LeftNibble {
                            4
                        } else {
                            0
                        };
                        if cursor.pos >= buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        // Change the selected nibble
                        if let Some(c) = key.to_digit(16) {
                            buf[cursor.pos] = buf[cursor.pos] & mask | (c as u8) << shift;
                        }
                    }
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::replaceend => {
                    clear = true;
                }
                Rule::remove => {
                    // check if in valid range
                    if buf.len() > 0 && cursor.pos < buf.len() {
                        // remove the current char
                        buf.remove(cursor.pos);
                    }
                    // always perform the movement if possible
                    if cursor.pos > 0 && cursor.pos >= buf.len() {
                        cursor.pos -= 1;
                    }
                    lastcommand = command.clone();
                }
                Rule::dd => {
                    let amount: usize = cmd.as_str().parse().unwrap_or(1);
                    // check if in valid range
                    if buf.len() > 0 && cursor.pos < buf.len() {
                        let startofline = cursor.pos - cursor.pos % COLS;
                        let mut endofline = cursor.pos - (cursor.pos % COLS) + (COLS * amount);
                        endofline = cmp::min(endofline, buf.len());
                        buf.drain(startofline..endofline);
                        if buf.len() <= 0 {
                            cursor.pos = 0; // Ensure cursor does not go below 0
                        }
                        if buf.len() > 0 && cursor.pos >= buf.len() {
                            cursor.pos = buf.len() - 1;
                        }
                    }
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::insert => {
                    // debug.push_str("next chars will be inserted!");
                    clear = false;
                }
                Rule::insertstuff => {
                    let key = command.chars().last().unwrap_or('x');

                    if cursor.sel == CursorSelects::LeftNibble {
                        // Left nibble
                        if let Some(c) = key.to_digit(16) {
                            buf.insert(cursor.pos, (c as u8) << 4);
                            cursor.sel = CursorSelects::RightNibble;
                        }
                    } else if cursor.sel == CursorSelects::RightNibble {
                        // Right nibble
                        if cursor.pos == buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        if let Some(c) = key.to_digit(16) {
                            buf[cursor.pos] = buf[cursor.pos] & 0xF0 | c as u8;
                            cursor.sel = CursorSelects::LeftNibble;
                            cursor.pos += 1;
                        }
                    } else if cursor.sel == CursorSelects::AsciiChar {
                        buf.insert(cursor.pos, key as u8);
                        cursor.pos += 1;
                    }

                    clear = false;
                }
                Rule::insertend => {
                    // debug.push_str(&format!("Insert ended. ({:?})", command.clone()));
                    lastcommand.clear();
                    lastcommand.push_str(&format!(
                        "Command repeation for {:?} not yet implemented.",
                        cmd.as_rule()
                    ));
                    clear = true;
                }
                Rule::jumpascii => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        cursor.sel = CursorSelects::LeftNibble;
                    } else {
                        cursor.sel = CursorSelects::AsciiChar;
                    }
                }
                Rule::helpfile => {
                    command.pop();
                    command.push_str("No helpfile yet");
                    clear = false;
                }
                Rule::repeat => {
                    command = lastcommand.clone();
                    clear = false;
                    autoparse = true;
                }
                Rule::gg => {
                    let linenr: usize = cmd.as_str().parse().unwrap_or(0);
                    cursor.pos = linenr * COLS; // jump to the line
                    if cursor.pos > buf.len() {
                        // detect file end
                        cursor.pos = buf.len();
                    }
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                    clear = true;
                }
                Rule::backspace => {
                    command.pop();
                    command.pop();
                    clear = false;
                }
                Rule::saveandexit => {
                    save = true;
                    quitnow = true;
                }
                Rule::exit => quitnow = true,
                Rule::save => save = true,
                Rule::escape => (),
                _ => (),
            }

            for inner_cmd in cmd.into_inner() {
                match inner_cmd.as_rule() {
                    Rule::searchstr => {
                        let search = inner_cmd.as_str().as_bytes();
                        let foundpos = TwoWaySearcher::new(&search);
                        cursor.pos = foundpos.search_in(&buf).unwrap_or(cursor.pos);
                    }
                    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);
                        }
                        cursor.pos = buf.find_subset(&needle).unwrap_or(cursor.pos);
                        // debug.push_str(&format!("Searching for: {:?}", needle ));
                    }
                    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 cursor.pos > (screenheight + screenoffset - 1) * COLS - 1 {
                screenoffset = 2 + cursor.pos / COLS - screenheight;
            }
            if cursor.pos < screenoffset * COLS {
                screenoffset = cursor.pos / COLS;
            }
        }

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

    refresh();
    endwin();
}
<commit_msg>Add info about repeation '.' of insert 'i'<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 CursorSelects {
    LeftNibble,
    RightNibble,
    AsciiChar,
}

#[derive(Copy, Clone)]
pub struct CursorState {
    pos: usize,
    sel: CursorSelects,
}

fn main() {
    const VERSION: &str = env!("CARGO_PKG_VERSION");
    let mut buf = vec![];
    let mut cursor = CursorState {
        pos: 0,
        sel: CursorSelects::LeftNibble,
    };
    // 0 = display data from first line of file
    let mut screenoffset: usize = 0;
    const COLS: 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(), COLS, screenoffset);
    draw(
        &buf[draw_range.0..draw_range.1],
        COLS,
        &command,
        &mut debug,
        cursor,
        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 cursor.pos + COLS < buf.len() {
                        // not at end
                        cursor.pos += COLS;
                    } else {
                        // when at end
                        if buf.len() != 0 {
                            // Suppress underflow
                            cursor.pos = buf.len() - 1;
                        }
                    }
                }
                Rule::up => {
                    if cursor.pos >= COLS {
                        cursor.pos -= COLS;
                    }
                }
                Rule::left => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos > 0 {
                            cursor.pos -= 1;
                        }
                    } else if cursor.sel == CursorSelects::RightNibble {
                        cursor.sel = CursorSelects::LeftNibble;
                    } else if cursor.sel == CursorSelects::LeftNibble {
                        if cursor.pos > 0 {
                            // not at start
                            cursor.sel = CursorSelects::RightNibble;
                            cursor.pos -= 1;
                        }
                    }
                }
                Rule::right => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos + 1 < buf.len() {
                            // not at end
                            cursor.pos += 1;
                        }
                    } else if cursor.sel == CursorSelects::LeftNibble {
                        cursor.sel = CursorSelects::RightNibble;
                    } else if cursor.sel == CursorSelects::RightNibble {
                        if cursor.pos + 1 < buf.len() {
                            // not at end
                            cursor.sel = CursorSelects::LeftNibble;
                            cursor.pos += 1;
                        }
                    }
                }
                Rule::start => {
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                    if cursor.sel == CursorSelects::RightNibble {
                        cursor.sel = CursorSelects::LeftNibble;
                    }
                }
                Rule::end => {
                    // check if no overflow
                    if cursor.pos - (cursor.pos % COLS) + (COLS - 1) < buf.len() {
                        // jump to end of line
                        cursor.pos = cursor.pos - (cursor.pos % COLS) + (COLS - 1);
                    } else {
                        // jump to end of line
                        cursor.pos = buf.len() - 1
                    }
                    if cursor.sel == CursorSelects::LeftNibble {
                        cursor.sel = CursorSelects::RightNibble;
                    }
                }
                Rule::bottom => {
                    cursor.pos = buf.len() - 1;
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                }
                Rule::replace => {
                    clear = false;
                }
                Rule::replacement => {
                    let key = command.chars().last().unwrap_or('x');
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos >= buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        buf[cursor.pos] = key as u8;
                    } else {
                        let mask = if cursor.sel == CursorSelects::LeftNibble {
                            0x0F
                        } else {
                            0xF0
                        };
                        let shift = if cursor.sel == CursorSelects::LeftNibble {
                            4
                        } else {
                            0
                        };
                        if cursor.pos >= buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        // Change the selected nibble
                        if let Some(c) = key.to_digit(16) {
                            buf[cursor.pos] = buf[cursor.pos] & mask | (c as u8) << shift;
                        }
                    }
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::replaceend => {
                    clear = true;
                }
                Rule::remove => {
                    // check if in valid range
                    if buf.len() > 0 && cursor.pos < buf.len() {
                        // remove the current char
                        buf.remove(cursor.pos);
                    }
                    // always perform the movement if possible
                    if cursor.pos > 0 && cursor.pos >= buf.len() {
                        cursor.pos -= 1;
                    }
                    lastcommand = command.clone();
                }
                Rule::dd => {
                    let amount: usize = cmd.as_str().parse().unwrap_or(1);
                    // check if in valid range
                    if buf.len() > 0 && cursor.pos < buf.len() {
                        let startofline = cursor.pos - cursor.pos % COLS;
                        let mut endofline = cursor.pos - (cursor.pos % COLS) + (COLS * amount);
                        endofline = cmp::min(endofline, buf.len());
                        buf.drain(startofline..endofline);
                        if buf.len() <= 0 {
                            cursor.pos = 0; // Ensure cursor does not go below 0
                        }
                        if buf.len() > 0 && cursor.pos >= buf.len() {
                            cursor.pos = buf.len() - 1;
                        }
                    }
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::insert => {
                    // debug.push_str("next chars will be inserted!");
                    clear = false;
                }
                Rule::insertstuff => {
                    let key = command.chars().last().unwrap_or('x');

                    if cursor.sel == CursorSelects::LeftNibble {
                        // Left nibble
                        if let Some(c) = key.to_digit(16) {
                            buf.insert(cursor.pos, (c as u8) << 4);
                            cursor.sel = CursorSelects::RightNibble;
                        }
                    } else if cursor.sel == CursorSelects::RightNibble {
                        // Right nibble
                        if cursor.pos == buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        if let Some(c) = key.to_digit(16) {
                            buf[cursor.pos] = buf[cursor.pos] & 0xF0 | c as u8;
                            cursor.sel = CursorSelects::LeftNibble;
                            cursor.pos += 1;
                        }
                    } else if cursor.sel == CursorSelects::AsciiChar {
                        buf.insert(cursor.pos, key as u8);
                        cursor.pos += 1;
                    }

                    clear = false;
                }
                Rule::insertend => {
                    // The reason this does not work is the escape character at the end
                    // lastcommand = command.clone();
                    lastcommand.clear();
                    lastcommand.push_str(&format!(
                        "Command repeation for {:?} not yet implemented.",
                        cmd.as_rule()
                    ));
                    clear = true;
                }
                Rule::jumpascii => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        cursor.sel = CursorSelects::LeftNibble;
                    } else {
                        cursor.sel = CursorSelects::AsciiChar;
                    }
                }
                Rule::helpfile => {
                    command.pop();
                    command.push_str("No helpfile yet");
                    clear = false;
                }
                Rule::repeat => {
                    command = lastcommand.clone();
                    clear = false;
                    autoparse = true;
                }
                Rule::gg => {
                    let linenr: usize = cmd.as_str().parse().unwrap_or(0);
                    cursor.pos = linenr * COLS; // jump to the line
                    if cursor.pos > buf.len() {
                        // detect file end
                        cursor.pos = buf.len();
                    }
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                    clear = true;
                }
                Rule::backspace => {
                    command.pop();
                    command.pop();
                    clear = false;
                }
                Rule::saveandexit => {
                    save = true;
                    quitnow = true;
                }
                Rule::exit => quitnow = true,
                Rule::save => save = true,
                Rule::escape => (),
                _ => (),
            }

            for inner_cmd in cmd.into_inner() {
                match inner_cmd.as_rule() {
                    Rule::searchstr => {
                        let search = inner_cmd.as_str().as_bytes();
                        let foundpos = TwoWaySearcher::new(&search);
                        cursor.pos = foundpos.search_in(&buf).unwrap_or(cursor.pos);
                    }
                    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);
                        }
                        cursor.pos = buf.find_subset(&needle).unwrap_or(cursor.pos);
                        // debug.push_str(&format!("Searching for: {:?}", needle ));
                    }
                    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 cursor.pos > (screenheight + screenoffset - 1) * COLS - 1 {
                screenoffset = 2 + cursor.pos / COLS - screenheight;
            }
            if cursor.pos < screenoffset * COLS {
                screenoffset = cursor.pos / COLS;
            }
        }

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

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


<commit_before>
use ffi;
use super::enums::*;
use super::raw_vec::RawVec;
use super::util::FFTW_MUTEX;
use super::{c32, c64};

use std::marker::PhantomData;

pub struct Plan<A, B> {
    plan: RawPlan,
    phantom: PhantomData<(A, B)>,
}

impl<A, B> Plan<A, B> {
    /// this function modifys the array referred in plan creation
    pub unsafe fn execute(&self) { self.plan.execute() }
}

impl<T: R2RPlanCreate> Plan<T, T> {
    pub fn r2r_1d(n: usize,
                  mut in_: &mut RawVec<T>,
                  mut out: &mut RawVec<T>,
                  kind: R2R_KIND,
                  flag: FLAG)
                  -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { T::r2r_1d(n, &mut in_, &mut out, kind, flag) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

impl<T: C2CPlanCreate> Plan<T, T> {
    pub fn c2c_1d(n: usize,
                  mut in_: &mut RawVec<T>,
                  mut out: &mut RawVec<T>,
                  sign: SIGN,
                  flag: FLAG)
                  -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { T::c2c_1d(n, &mut in_, &mut out, sign, flag) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

impl<C, R> Plan<C, R>
    where (C, R): C2RPlanCreate<Real = R, Complex = C>
{
    pub fn c2r_1d(n: usize, in_: &mut RawVec<C>, out: &mut RawVec<R>, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { <(C, R)>::c2r_1d(n, in_, out, flag) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

impl<R, C> Plan<R, C>
    where (C, R): C2RPlanCreate<Real = R, Complex = C>
{
    pub fn r2c_1d(n: usize, in_: &mut RawVec<R>, out: &mut RawVec<C>, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { <(C, R)>::r2c_1d(n, in_, out, flag) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

pub enum RawPlan {
    _64(ffi::fftw_plan),
    _32(ffi::fftwf_plan),
}

impl RawPlan {
    pub unsafe fn execute(&self) {
        match *self {
            RawPlan::_64(p) => ffi::fftw_execute(p),
            RawPlan::_32(p) => ffi::fftwf_execute(p),
        }
    }
}

impl Drop for RawPlan {
    fn drop(&mut self) {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        unsafe {
            match *self {
                RawPlan::_64(p) => ffi::fftw_destroy_plan(p),
                RawPlan::_32(p) => ffi::fftwf_destroy_plan(p),
            }
        }
        drop(lock);
    }
}

pub trait R2RPlanCreate: Sized {
    unsafe fn r2r_1d(n: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     R2R_KIND,
                     FLAG)
                     -> RawPlan;
    unsafe fn r2r_2d(n0: usize,
                     n1: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     R2R_KIND,
                     R2R_KIND,
                     FLAG)
                     -> RawPlan;
    unsafe fn r2r_3d(n0: usize,
                     n1: usize,
                     n2: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     R2R_KIND,
                     R2R_KIND,
                     R2R_KIND,
                     FLAG)
                     -> RawPlan;
}
pub trait C2CPlanCreate: Sized {
    unsafe fn c2c_1d(n: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     SIGN,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2c_2d(n0: usize,
                     n1: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     SIGN,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2c_3d(n0: usize,
                     n1: usize,
                     n2: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     SIGN,
                     FLAG)
                     -> RawPlan;
}

pub trait C2RPlanCreate {
    type Real: Sized;
    type Complex: Sized;
    unsafe fn r2c_1d(n: usize,
                     in_: &mut RawVec<Self::Real>,
                     out: &mut RawVec<Self::Complex>,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2r_1d(n: usize,
                     in_: &mut RawVec<Self::Complex>,
                     out: &mut RawVec<Self::Real>,
                     FLAG)
                     -> RawPlan;
    unsafe fn r2c_2d(n0: usize,
                     n1: usize,
                     in_: &mut RawVec<Self::Real>,
                     out: &mut RawVec<Self::Complex>,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2r_2d(n0: usize,
                     n1: usize,
                     in_: &mut RawVec<Self::Complex>,
                     out: &mut RawVec<Self::Real>,
                     FLAG)
                     -> RawPlan;
    unsafe fn r2c_3d(n0: usize,
                     n1: usize,
                     n2: usize,
                     in_: &mut RawVec<Self::Real>,
                     out: &mut RawVec<Self::Complex>,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2r_3d(n0: usize,
                     n1: usize,
                     n2: usize,
                     in_: &mut RawVec<Self::Complex>,
                     out: &mut RawVec<Self::Real>,
                     FLAG)
                     -> RawPlan;
}

macro_rules! impl_plan_create {
    ($bit:ident, $float:ty, $complex:ty,
     $r2r_1d:ident, $r2c_1d:ident, $c2r_1d:ident, $c2c_1d:ident,
     $r2r_2d:ident, $r2c_2d:ident, $c2r_2d:ident, $c2c_2d:ident,
     $r2r_3d:ident, $r2c_3d:ident, $c2r_3d:ident, $c2c_3d:ident) => {

impl R2RPlanCreate for $float {
    unsafe fn r2r_1d(n: usize, in_: &mut RawVec<Self>, out: &mut RawVec<Self>, kind: R2R_KIND, flag: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2r_1d(n as i32, in_.as_mut_ptr(), out.as_mut_ptr(), kind, flag as u32))
    }
    unsafe fn r2r_2d(n0: usize, n1: usize, in_: &mut RawVec<Self>, out: &mut RawVec<Self>, k0: R2R_KIND, k1: R2R_KIND, flag: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2r_2d(n0 as i32, n1 as i32, in_.as_mut_ptr(), out.as_mut_ptr(), k0, k1, flag as u32))
    }
    unsafe fn r2r_3d(n0: usize, n1: usize, n2: usize, in_: &mut RawVec<Self>, out: &mut RawVec<Self>, k0: R2R_KIND, k1: R2R_KIND, k2: R2R_KIND, flag: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2r_3d(n0 as i32, n1 as i32, n2 as i32, in_.as_mut_ptr(), out.as_mut_ptr(), k0, k1, k2, flag as u32))
    }
}

impl C2CPlanCreate for $complex {
    unsafe fn c2c_1d(n: usize, i: &mut RawVec<Self>, o: &mut RawVec<Self>, s: SIGN, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2c_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), s as i32, f as u32))
    }
    unsafe fn c2c_2d(n0: usize, n1: usize, i: &mut RawVec<Self>, o: &mut RawVec<Self>, s: SIGN, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2c_2d(n0 as i32, n1 as i32, i.as_mut_ptr(), o.as_mut_ptr(), s as i32, f as u32))
    }
    unsafe fn c2c_3d(n0: usize, n1: usize, n2: usize, i: &mut RawVec<Self>, o: &mut RawVec<Self>, s: SIGN, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2c_3d(n0 as i32, n1 as i32, n2 as i32, i.as_mut_ptr(), o.as_mut_ptr(), s as i32, f as u32))
    }
}

impl C2RPlanCreate for ($complex, $float) {
    type Real = $float;
    type Complex = $complex;
    unsafe fn r2c_1d(n: usize, i: &mut RawVec<Self::Real>, o: &mut RawVec<Self::Complex>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2c_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn c2r_1d(n: usize, i: &mut RawVec<Self::Complex>, o: &mut RawVec<Self::Real>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2r_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn r2c_2d(n0: usize, n1: usize, i: &mut RawVec<Self::Real>, o: &mut RawVec<Self::Complex>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2c_2d(n0 as i32, n1 as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn c2r_2d(n0: usize, n1: usize, i: &mut RawVec<Self::Complex>, o: &mut RawVec<Self::Real>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2r_2d(n0 as i32, n1 as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn r2c_3d(n0: usize, n1: usize, n2: usize, i: &mut RawVec<Self::Real>, o: &mut RawVec<Self::Complex>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2c_3d(n0 as i32, n1 as i32, n2 as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn c2r_3d(n0: usize, n1: usize, n2: usize, i: &mut RawVec<Self::Complex>, o: &mut RawVec<Self::Real>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2r_3d(n0 as i32, n1 as i32, n2 as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
}

}} // impl_plan_create

impl_plan_create!(_64,
                  f64,
                  c64,
                  fftw_plan_r2r_1d,
                  fftw_plan_dft_r2c_1d,
                  fftw_plan_dft_c2r_1d,
                  fftw_plan_dft_1d,
                  fftw_plan_r2r_2d,
                  fftw_plan_dft_r2c_2d,
                  fftw_plan_dft_c2r_2d,
                  fftw_plan_dft_2d,
                  fftw_plan_r2r_3d,
                  fftw_plan_dft_r2c_3d,
                  fftw_plan_dft_c2r_3d,
                  fftw_plan_dft_3d);
impl_plan_create!(_32,
                  f32,
                  c32,
                  fftwf_plan_r2r_1d,
                  fftwf_plan_dft_r2c_1d,
                  fftwf_plan_dft_c2r_1d,
                  fftwf_plan_dft_1d,
                  fftwf_plan_r2r_2d,
                  fftwf_plan_dft_r2c_2d,
                  fftwf_plan_dft_c2r_2d,
                  fftwf_plan_dft_2d,
                  fftwf_plan_r2r_3d,
                  fftwf_plan_dft_r2c_3d,
                  fftwf_plan_dft_c2r_3d,
                  fftwf_plan_dft_3d);
<commit_msg>Shorten code using _* naming<commit_after>
use ffi;
use super::enums::*;
use super::raw_vec::RawVec;
use super::util::FFTW_MUTEX;
use super::{c32, c64};

use std::marker::PhantomData;

pub struct Plan<A, B> {
    plan: RawPlan,
    phantom: PhantomData<(A, B)>,
}

impl<A, B> Plan<A, B> {
    /// this function modifys the array referred in plan creation
    pub unsafe fn execute(&self) { self.plan.execute() }
}

impl<T: R2RPlanCreate> Plan<T, T> {
    pub fn r2r_1d(n: usize,
                  mut in_: &mut RawVec<T>,
                  mut out: &mut RawVec<T>,
                  kind: R2R_KIND,
                  flag: FLAG)
                  -> Self {
        let _lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        Plan {
            plan: unsafe { T::r2r_1d(n, &mut in_, &mut out, kind, flag) },
            phantom: PhantomData,
        }
    }
}

impl<T: C2CPlanCreate> Plan<T, T> {
    pub fn c2c_1d(n: usize,
                  mut in_: &mut RawVec<T>,
                  mut out: &mut RawVec<T>,
                  sign: SIGN,
                  flag: FLAG)
                  -> Self {
        let _lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        Plan {
            plan: unsafe { T::c2c_1d(n, &mut in_, &mut out, sign, flag) },
            phantom: PhantomData,
        }
    }
}

impl<C, R> Plan<C, R>
    where (C, R): C2RPlanCreate<Real = R, Complex = C>
{
    pub fn c2r_1d(n: usize, in_: &mut RawVec<C>, out: &mut RawVec<R>, flag: FLAG) -> Self {
        let _lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        Plan {
            plan: unsafe { <(C, R)>::c2r_1d(n, in_, out, flag) },
            phantom: PhantomData,
        }
    }
}

impl<R, C> Plan<R, C>
    where (C, R): C2RPlanCreate<Real = R, Complex = C>
{
    pub fn r2c_1d(n: usize, in_: &mut RawVec<R>, out: &mut RawVec<C>, flag: FLAG) -> Self {
        let _lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        Plan {
            plan: unsafe { <(C, R)>::r2c_1d(n, in_, out, flag) },
            phantom: PhantomData,
        }
    }
}

pub enum RawPlan {
    _64(ffi::fftw_plan),
    _32(ffi::fftwf_plan),
}

impl RawPlan {
    pub unsafe fn execute(&self) {
        match *self {
            RawPlan::_64(p) => ffi::fftw_execute(p),
            RawPlan::_32(p) => ffi::fftwf_execute(p),
        }
    }
}

impl Drop for RawPlan {
    fn drop(&mut self) {
        let _lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        unsafe {
            match *self {
                RawPlan::_64(p) => ffi::fftw_destroy_plan(p),
                RawPlan::_32(p) => ffi::fftwf_destroy_plan(p),
            }
        }
    }
}

pub trait R2RPlanCreate: Sized {
    unsafe fn r2r_1d(n: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     R2R_KIND,
                     FLAG)
                     -> RawPlan;
    unsafe fn r2r_2d(n0: usize,
                     n1: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     R2R_KIND,
                     R2R_KIND,
                     FLAG)
                     -> RawPlan;
    unsafe fn r2r_3d(n0: usize,
                     n1: usize,
                     n2: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     R2R_KIND,
                     R2R_KIND,
                     R2R_KIND,
                     FLAG)
                     -> RawPlan;
}
pub trait C2CPlanCreate: Sized {
    unsafe fn c2c_1d(n: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     SIGN,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2c_2d(n0: usize,
                     n1: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     SIGN,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2c_3d(n0: usize,
                     n1: usize,
                     n2: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     SIGN,
                     FLAG)
                     -> RawPlan;
}

pub trait C2RPlanCreate {
    type Real: Sized;
    type Complex: Sized;
    unsafe fn r2c_1d(n: usize,
                     in_: &mut RawVec<Self::Real>,
                     out: &mut RawVec<Self::Complex>,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2r_1d(n: usize,
                     in_: &mut RawVec<Self::Complex>,
                     out: &mut RawVec<Self::Real>,
                     FLAG)
                     -> RawPlan;
    unsafe fn r2c_2d(n0: usize,
                     n1: usize,
                     in_: &mut RawVec<Self::Real>,
                     out: &mut RawVec<Self::Complex>,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2r_2d(n0: usize,
                     n1: usize,
                     in_: &mut RawVec<Self::Complex>,
                     out: &mut RawVec<Self::Real>,
                     FLAG)
                     -> RawPlan;
    unsafe fn r2c_3d(n0: usize,
                     n1: usize,
                     n2: usize,
                     in_: &mut RawVec<Self::Real>,
                     out: &mut RawVec<Self::Complex>,
                     FLAG)
                     -> RawPlan;
    unsafe fn c2r_3d(n0: usize,
                     n1: usize,
                     n2: usize,
                     in_: &mut RawVec<Self::Complex>,
                     out: &mut RawVec<Self::Real>,
                     FLAG)
                     -> RawPlan;
}

macro_rules! impl_plan_create {
    ($bit:ident, $float:ty, $complex:ty,
     $r2r_1d:ident, $r2c_1d:ident, $c2r_1d:ident, $c2c_1d:ident,
     $r2r_2d:ident, $r2c_2d:ident, $c2r_2d:ident, $c2c_2d:ident,
     $r2r_3d:ident, $r2c_3d:ident, $c2r_3d:ident, $c2c_3d:ident) => {

impl R2RPlanCreate for $float {
    unsafe fn r2r_1d(n: usize, in_: &mut RawVec<Self>, out: &mut RawVec<Self>, kind: R2R_KIND, flag: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2r_1d(n as i32, in_.as_mut_ptr(), out.as_mut_ptr(), kind, flag as u32))
    }
    unsafe fn r2r_2d(n0: usize, n1: usize, in_: &mut RawVec<Self>, out: &mut RawVec<Self>, k0: R2R_KIND, k1: R2R_KIND, flag: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2r_2d(n0 as i32, n1 as i32, in_.as_mut_ptr(), out.as_mut_ptr(), k0, k1, flag as u32))
    }
    unsafe fn r2r_3d(n0: usize, n1: usize, n2: usize, in_: &mut RawVec<Self>, out: &mut RawVec<Self>, k0: R2R_KIND, k1: R2R_KIND, k2: R2R_KIND, flag: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2r_3d(n0 as i32, n1 as i32, n2 as i32, in_.as_mut_ptr(), out.as_mut_ptr(), k0, k1, k2, flag as u32))
    }
}

impl C2CPlanCreate for $complex {
    unsafe fn c2c_1d(n: usize, i: &mut RawVec<Self>, o: &mut RawVec<Self>, s: SIGN, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2c_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), s as i32, f as u32))
    }
    unsafe fn c2c_2d(n0: usize, n1: usize, i: &mut RawVec<Self>, o: &mut RawVec<Self>, s: SIGN, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2c_2d(n0 as i32, n1 as i32, i.as_mut_ptr(), o.as_mut_ptr(), s as i32, f as u32))
    }
    unsafe fn c2c_3d(n0: usize, n1: usize, n2: usize, i: &mut RawVec<Self>, o: &mut RawVec<Self>, s: SIGN, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2c_3d(n0 as i32, n1 as i32, n2 as i32, i.as_mut_ptr(), o.as_mut_ptr(), s as i32, f as u32))
    }
}

impl C2RPlanCreate for ($complex, $float) {
    type Real = $float;
    type Complex = $complex;
    unsafe fn r2c_1d(n: usize, i: &mut RawVec<Self::Real>, o: &mut RawVec<Self::Complex>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2c_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn c2r_1d(n: usize, i: &mut RawVec<Self::Complex>, o: &mut RawVec<Self::Real>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2r_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn r2c_2d(n0: usize, n1: usize, i: &mut RawVec<Self::Real>, o: &mut RawVec<Self::Complex>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2c_2d(n0 as i32, n1 as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn c2r_2d(n0: usize, n1: usize, i: &mut RawVec<Self::Complex>, o: &mut RawVec<Self::Real>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2r_2d(n0 as i32, n1 as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn r2c_3d(n0: usize, n1: usize, n2: usize, i: &mut RawVec<Self::Real>, o: &mut RawVec<Self::Complex>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$r2c_3d(n0 as i32, n1 as i32, n2 as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
    unsafe fn c2r_3d(n0: usize, n1: usize, n2: usize, i: &mut RawVec<Self::Complex>, o: &mut RawVec<Self::Real>, f: FLAG) -> RawPlan {
        RawPlan::$bit(ffi::$c2r_3d(n0 as i32, n1 as i32, n2 as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32))
    }
}

}} // impl_plan_create

impl_plan_create!(_64,
                  f64,
                  c64,
                  fftw_plan_r2r_1d,
                  fftw_plan_dft_r2c_1d,
                  fftw_plan_dft_c2r_1d,
                  fftw_plan_dft_1d,
                  fftw_plan_r2r_2d,
                  fftw_plan_dft_r2c_2d,
                  fftw_plan_dft_c2r_2d,
                  fftw_plan_dft_2d,
                  fftw_plan_r2r_3d,
                  fftw_plan_dft_r2c_3d,
                  fftw_plan_dft_c2r_3d,
                  fftw_plan_dft_3d);
impl_plan_create!(_32,
                  f32,
                  c32,
                  fftwf_plan_r2r_1d,
                  fftwf_plan_dft_r2c_1d,
                  fftwf_plan_dft_c2r_1d,
                  fftwf_plan_dft_1d,
                  fftwf_plan_r2r_2d,
                  fftwf_plan_dft_r2c_2d,
                  fftwf_plan_dft_c2r_2d,
                  fftwf_plan_dft_2d,
                  fftwf_plan_r2r_3d,
                  fftwf_plan_dft_r2c_3d,
                  fftwf_plan_dft_c2r_3d,
                  fftwf_plan_dft_3d);
<|endoftext|>


<commit_before>#![feature(tuple_indexing)]
#![feature(macro_rules)]
#![feature(phase)]
#![feature(globs)]

#[phase(plugin, link)]
extern crate log;
extern crate getopts;
extern crate gl;
extern crate libc;
extern crate native;
extern crate sdl2;
extern crate time;

use ctrl::GameController;
use getopts::{optopt,optflag,getopts, usage};
use level::Level;
use libc::c_void;
use mat4::Mat4;
use numvec::Vec3;
use player::Player;
use sdl2::scancode;
use std::default::Default;
use std::os;
use wad::TextureDirectory;

#[macro_escape]
pub mod check_gl;
pub mod camera;
pub mod common;
pub mod ctrl;
pub mod mat4;
pub mod numvec;
pub mod player;
pub mod shader;
pub mod wad;
pub mod level;
pub mod vbo;
pub mod line;
pub mod texture;
pub mod render;

static WINDOW_TITLE: &'static str = "Rusty Doom v0.0.6";
static OPENGL_MAJOR_VERISON: int = 3;
static OPENGL_MINOR_VERISON: int = 3;
static OPENGL_DEPTH_SIZE: int = 24;

pub struct MainWindow {
    window: sdl2::video::Window,
    _context: sdl2::video::GLContext,
}
impl MainWindow {
    pub fn new(width: uint, height: uint) -> MainWindow {
        sdl2::video::gl_set_attribute(sdl2::video::GLContextMajorVersion,
                                      OPENGL_MAJOR_VERISON);
        sdl2::video::gl_set_attribute(sdl2::video::GLContextMinorVersion,
                                      OPENGL_MINOR_VERISON);
        sdl2::video::gl_set_attribute(sdl2::video::GLDepthSize,
                                      OPENGL_DEPTH_SIZE);
        sdl2::video::gl_set_attribute(sdl2::video::GLDoubleBuffer, 1);

        let window = sdl2::video::Window::new(
            WINDOW_TITLE, sdl2::video::PosCentered, sdl2::video::PosCentered,
            width as int, height as int,
            sdl2::video::OpenGL | sdl2::video::Shown).unwrap();

        let context = window.gl_create_context().unwrap();
        sdl2::clear_error();
        gl::load_with(|name| {
            match sdl2::video::gl_get_proc_address(name) {
                Some(glproc) => glproc as *const libc::c_void,
                None => {
                    warn!("missing GL function: {}", name);
                    std::ptr::null()
                }
            }
        });
        unsafe {
            let mut vao_id = 0;
            check_gl!(gl::GenVertexArrays(1, &mut vao_id));
            check_gl!(gl::BindVertexArray(vao_id));
        }
        MainWindow {
           window: window,
            _context: context,
        }
    }

    pub fn aspect_ratio(&self) -> f32 {
        let (w, h) = self.window.get_size();
        w as f32 / h as f32
    }

    pub fn swap_buffers(&self) {
        self.window.gl_swap_window();
    }
}

pub struct GameConfig<'a> {
    wad: &'a str,
    level_index: uint,
    fov: f32,
}

pub struct Game {
    window: MainWindow,
    player: Player,
    level: Level,
}
impl Game {
    pub fn new<'a>(window: MainWindow, config: GameConfig<'a>) -> Game {
        let mut wad = wad::Archive::open(&Path::new(config.wad)).unwrap();
        let textures = TextureDirectory::from_archive(&mut wad).unwrap();
        let level_name = *wad.get_level_name(config.level_index);
        let level = Level::new(&mut wad, &textures, &level_name);

        check_gl!(gl::ClearColor(0.06, 0.07, 0.09, 0.0));
        check_gl!(gl::Enable(gl::DEPTH_TEST));
        check_gl!(gl::DepthFunc(gl::LESS));

        let start = *level.get_start_pos();
        let mut player = Player::new(config.fov, window.aspect_ratio(),
                                     Default::default());
        player.set_position(&Vec3::new(start.x, 0.3, start.y));

        Game {
            window: window,
            player: player,
            level: level
        }
    }

    pub fn run(&mut self) {
        let quit_gesture = ctrl::AnyGesture(
            vec![ctrl::QuitTrigger,
                 ctrl::KeyTrigger(scancode::EscapeScanCode)]);

        let mut cum_time = 0.0;
        let mut cum_updates_time = 0.0;
        let mut num_frames = 0.0;
        let mut t0 = 0.0;
        let mut control = GameController::new();
        loop {
            check_gl!(gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT));
            let t1 = time::precise_time_s();
            let mut delta = (t1 - t0) as f32;
            if delta < 1e-10 { delta = 1.0 / 60.0; }
            let delta = delta;
            t0 = t1;

            let updates_t0 = time::precise_time_s();

            control.update();
            if control.poll_gesture(&quit_gesture) {
                break;
            }
            self.player.update(delta, &control);
            self.level.render(
                delta,
                &self.player.get_camera()
                .multiply_transform(&Mat4::new_identity()));

            let updates_t1 = time::precise_time_s();
            cum_updates_time += updates_t1 - updates_t0;

            cum_time += delta as f64;
            num_frames += 1.0 as f64;
            if cum_time > 2.0 {
                let fps = num_frames / cum_time;
                let cpums = 1000.0 * cum_updates_time / num_frames as f64;
                info!("Frame time: {:.2}ms ({:.2}ms cpu, FPS: {:.2})",
                      1000.0 / fps, cpums, fps);
                cum_time = 0.0;
                cum_updates_time = 0.0;
                num_frames = 0.0;
            }

            self.window.swap_buffers();
        }
    }
}

#[cfg(not(test))]
fn main() {
    let args: Vec<String> = os::args();
    let opts = [
        optopt("i", "iwad",
               "set initial wad file to use wad [default='doom1.wad']", "FILE"),
        optopt("l", "level",
               "the index of the level to render [default=0]", "N"),
        optopt("f", "fov",
               "horizontal field of view to please TotalHalibut [default=65]",
               "FLOAT"),
        optopt("r", "resolution",
               "the resolution at which to render the game [default=1280x720]",
               "WIDTHxHEIGHT"),
        optflag("d", "dump-levels", "list all levels and exit."),
        optflag("", "load-all", "loads all levels and exit; for debugging"),
        optflag("h", "help", "print this help message and exit"),
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => fail!(f.to_string()),
    };

    let wad_filename = matches
        .opt_str("i")
        .unwrap_or("doom1.wad".to_string());
    let (width, height) = matches
        .opt_str("r")
        .map(|r| {
            let v = r.as_slice().splitn(1, 'x').collect::<Vec<&str>>();
            if v.len() != 2 { None } else { Some(v) }
            .and_then(|v| from_str::<uint>(v[0]).map(|v0| (v0, v[1])))
            .and_then(|(v0, s)| from_str::<uint>(s).map(|v1| (v0, v1)))
            .expect("Invalid format for resolution, please use WIDTHxHEIGHT.")
        })
        .unwrap_or((1280, 720));
    let level_index = matches
        .opt_str("l")
        .map(|l| from_str::<uint>(l.as_slice())
            .expect("Invalid value for --level. Expected integer."))
        .unwrap_or(0);
    let fov = matches
        .opt_str("f")
        .map(|f| from_str::<f32>(f.as_slice())
             .expect("Invalid value for --fov. Expected float."))
        .unwrap_or(65.0);

    if matches.opt_present("h") {
        println!("{}", usage("A rust doom renderer.", &opts));
        return;
    }

    if matches.opt_present("d") {
        let wad = wad::Archive::open(&Path::new(wad_filename)).unwrap();
        for i_level in range(0, wad.num_levels()) {
            println!("{:3} {:8}", i_level, wad.get_level_name(i_level));
        }
        return;
    }

    if matches.opt_present("load-all") {
        if !sdl2::init(sdl2::InitVideo) {
            fail!("main: sdl video init failed.");
        }
        let _win = MainWindow::new(width, height);
        let t0 = time::precise_time_s();
        let mut wad = wad::Archive::open(
            &Path::new(wad_filename.as_slice())).unwrap();
        let textures = TextureDirectory::from_archive(&mut wad).unwrap();
        for level_index in range(0, wad.num_levels()) {
            let level_name = *wad.get_level_name(level_index);
            let level = Level::new(&mut wad, &textures, &level_name);
        }
        println!("Done, loaded all levels in {:.4}s. Shutting down...",
                 time::precise_time_s() - t0);
        sdl2::quit();
        return;
    }

    if !sdl2::init(sdl2::InitVideo) { fail!("main: sdl video init failed."); }

    let mut game = Game::new(
        MainWindow::new(width, height),
        GameConfig {
            wad: wad_filename.as_slice(),
            level_index: level_index,
            fov: fov,
        });
    game.run();

    info!("Shutting down.");
    drop(game);
    sdl2::quit();
}

//#[start]
//fn start(argc: int, argv: *const *const u8) -> int {
//    native::start(argc, argv, main)
//}

<commit_msg>Added serialize crate back fixing build.<commit_after>#![feature(tuple_indexing)]
#![feature(macro_rules)]
#![feature(phase)]
#![feature(globs)]

#[phase(plugin, link)]
extern crate log;
extern crate getopts;
extern crate gl;
extern crate libc;
extern crate native;
extern crate sdl2;
extern crate serialize;
extern crate time;

use ctrl::GameController;
use getopts::{optopt,optflag,getopts, usage};
use level::Level;
use libc::c_void;
use mat4::Mat4;
use numvec::Vec3;
use player::Player;
use sdl2::scancode;
use std::default::Default;
use std::os;
use wad::TextureDirectory;

#[macro_escape]
pub mod check_gl;
pub mod camera;
pub mod common;
pub mod ctrl;
pub mod mat4;
pub mod numvec;
pub mod player;
pub mod shader;
pub mod wad;
pub mod level;
pub mod vbo;
pub mod line;
pub mod texture;
pub mod render;

static WINDOW_TITLE: &'static str = "Rusty Doom v0.0.6";
static OPENGL_MAJOR_VERISON: int = 3;
static OPENGL_MINOR_VERISON: int = 3;
static OPENGL_DEPTH_SIZE: int = 24;

pub struct MainWindow {
    window: sdl2::video::Window,
    _context: sdl2::video::GLContext,
}
impl MainWindow {
    pub fn new(width: uint, height: uint) -> MainWindow {
        sdl2::video::gl_set_attribute(sdl2::video::GLContextMajorVersion,
                                      OPENGL_MAJOR_VERISON);
        sdl2::video::gl_set_attribute(sdl2::video::GLContextMinorVersion,
                                      OPENGL_MINOR_VERISON);
        sdl2::video::gl_set_attribute(sdl2::video::GLDepthSize,
                                      OPENGL_DEPTH_SIZE);
        sdl2::video::gl_set_attribute(sdl2::video::GLDoubleBuffer, 1);

        let window = sdl2::video::Window::new(
            WINDOW_TITLE, sdl2::video::PosCentered, sdl2::video::PosCentered,
            width as int, height as int,
            sdl2::video::OpenGL | sdl2::video::Shown).unwrap();

        let context = window.gl_create_context().unwrap();
        sdl2::clear_error();
        gl::load_with(|name| {
            match sdl2::video::gl_get_proc_address(name) {
                Some(glproc) => glproc as *const libc::c_void,
                None => {
                    warn!("missing GL function: {}", name);
                    std::ptr::null()
                }
            }
        });
        unsafe {
            let mut vao_id = 0;
            check_gl!(gl::GenVertexArrays(1, &mut vao_id));
            check_gl!(gl::BindVertexArray(vao_id));
        }
        MainWindow {
           window: window,
            _context: context,
        }
    }

    pub fn aspect_ratio(&self) -> f32 {
        let (w, h) = self.window.get_size();
        w as f32 / h as f32
    }

    pub fn swap_buffers(&self) {
        self.window.gl_swap_window();
    }
}

pub struct GameConfig<'a> {
    wad: &'a str,
    level_index: uint,
    fov: f32,
}

pub struct Game {
    window: MainWindow,
    player: Player,
    level: Level,
}
impl Game {
    pub fn new<'a>(window: MainWindow, config: GameConfig<'a>) -> Game {
        let mut wad = wad::Archive::open(&Path::new(config.wad)).unwrap();
        let textures = TextureDirectory::from_archive(&mut wad).unwrap();
        let level_name = *wad.get_level_name(config.level_index);
        let level = Level::new(&mut wad, &textures, &level_name);

        check_gl!(gl::ClearColor(0.06, 0.07, 0.09, 0.0));
        check_gl!(gl::Enable(gl::DEPTH_TEST));
        check_gl!(gl::DepthFunc(gl::LESS));

        let start = *level.get_start_pos();
        let mut player = Player::new(config.fov, window.aspect_ratio(),
                                     Default::default());
        player.set_position(&Vec3::new(start.x, 0.3, start.y));

        Game {
            window: window,
            player: player,
            level: level
        }
    }

    pub fn run(&mut self) {
        let quit_gesture = ctrl::AnyGesture(
            vec![ctrl::QuitTrigger,
                 ctrl::KeyTrigger(scancode::EscapeScanCode)]);

        let mut cum_time = 0.0;
        let mut cum_updates_time = 0.0;
        let mut num_frames = 0.0;
        let mut t0 = 0.0;
        let mut control = GameController::new();
        loop {
            check_gl!(gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT));
            let t1 = time::precise_time_s();
            let mut delta = (t1 - t0) as f32;
            if delta < 1e-10 { delta = 1.0 / 60.0; }
            let delta = delta;
            t0 = t1;

            let updates_t0 = time::precise_time_s();

            control.update();
            if control.poll_gesture(&quit_gesture) {
                break;
            }
            self.player.update(delta, &control);
            self.level.render(
                delta,
                &self.player.get_camera()
                .multiply_transform(&Mat4::new_identity()));

            let updates_t1 = time::precise_time_s();
            cum_updates_time += updates_t1 - updates_t0;

            cum_time += delta as f64;
            num_frames += 1.0 as f64;
            if cum_time > 2.0 {
                let fps = num_frames / cum_time;
                let cpums = 1000.0 * cum_updates_time / num_frames as f64;
                info!("Frame time: {:.2}ms ({:.2}ms cpu, FPS: {:.2})",
                      1000.0 / fps, cpums, fps);
                cum_time = 0.0;
                cum_updates_time = 0.0;
                num_frames = 0.0;
            }

            self.window.swap_buffers();
        }
    }
}

#[cfg(not(test))]
fn main() {
    let args: Vec<String> = os::args();
    let opts = [
        optopt("i", "iwad",
               "set initial wad file to use wad [default='doom1.wad']", "FILE"),
        optopt("l", "level",
               "the index of the level to render [default=0]", "N"),
        optopt("f", "fov",
               "horizontal field of view to please TotalHalibut [default=65]",
               "FLOAT"),
        optopt("r", "resolution",
               "the resolution at which to render the game [default=1280x720]",
               "WIDTHxHEIGHT"),
        optflag("d", "dump-levels", "list all levels and exit."),
        optflag("", "load-all", "loads all levels and exit; for debugging"),
        optflag("h", "help", "print this help message and exit"),
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => fail!(f.to_string()),
    };

    let wad_filename = matches
        .opt_str("i")
        .unwrap_or("doom1.wad".to_string());
    let (width, height) = matches
        .opt_str("r")
        .map(|r| {
            let v = r.as_slice().splitn(1, 'x').collect::<Vec<&str>>();
            if v.len() != 2 { None } else { Some(v) }
            .and_then(|v| from_str::<uint>(v[0]).map(|v0| (v0, v[1])))
            .and_then(|(v0, s)| from_str::<uint>(s).map(|v1| (v0, v1)))
            .expect("Invalid format for resolution, please use WIDTHxHEIGHT.")
        })
        .unwrap_or((1280, 720));
    let level_index = matches
        .opt_str("l")
        .map(|l| from_str::<uint>(l.as_slice())
            .expect("Invalid value for --level. Expected integer."))
        .unwrap_or(0);
    let fov = matches
        .opt_str("f")
        .map(|f| from_str::<f32>(f.as_slice())
             .expect("Invalid value for --fov. Expected float."))
        .unwrap_or(65.0);

    if matches.opt_present("h") {
        println!("{}", usage("A rust doom renderer.", &opts));
        return;
    }

    if matches.opt_present("d") {
        let wad = wad::Archive::open(&Path::new(wad_filename)).unwrap();
        for i_level in range(0, wad.num_levels()) {
            println!("{:3} {:8}", i_level, wad.get_level_name(i_level));
        }
        return;
    }

    if matches.opt_present("load-all") {
        if !sdl2::init(sdl2::InitVideo) {
            fail!("main: sdl video init failed.");
        }
        let _win = MainWindow::new(width, height);
        let t0 = time::precise_time_s();
        let mut wad = wad::Archive::open(
            &Path::new(wad_filename.as_slice())).unwrap();
        let textures = TextureDirectory::from_archive(&mut wad).unwrap();
        for level_index in range(0, wad.num_levels()) {
            let level_name = *wad.get_level_name(level_index);
            let level = Level::new(&mut wad, &textures, &level_name);
        }
        println!("Done, loaded all levels in {:.4}s. Shutting down...",
                 time::precise_time_s() - t0);
        sdl2::quit();
        return;
    }

    if !sdl2::init(sdl2::InitVideo) { fail!("main: sdl video init failed."); }

    let mut game = Game::new(
        MainWindow::new(width, height),
        GameConfig {
            wad: wad_filename.as_slice(),
            level_index: level_index,
            fov: fov,
        });
    game.run();

    info!("Shutting down.");
    drop(game);
    sdl2::quit();
}

//#[start]
//fn start(argc: int, argv: *const *const u8) -> int {
//    native::start(argc, argv, main)
//}

<|endoftext|>


<commit_before>#![feature(decode_utf8)]
#![feature(io)]

extern crate argparse;
extern crate bit_set;
extern crate byteorder;
extern crate compress;
extern crate libc;

use std::fs;
use std::io;
use std::mem;
use std::path;
use std::slice;

use argparse::{Store, StoreTrue};

use bit_set::BitSet;

use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

use libc::c_void;

//magic:
use std::io::Read;
use std::io::Seek;
use std::os::unix::io::AsRawFd;

type CharResult = Result<char, io::CharsError>;

static TRI_MAX: usize = 64 * 64 * 64;

fn simplify(wut: char) -> u8 {
    let c = match wut {
        'a' ... 'z' => (wut as u8 - 'a' as u8 + 'A' as u8) as char,
        _ => wut,
    };

    if c > 128 as char {
        if c.is_whitespace() {
            return 2;
        }
        if c.is_control() {
            return 0;
        }
        return 63;
    }

    let sym_end = 33u8;
    let letters = 21u8;

    match c {
        '\r' | '\n' => 1,
        '\t' | '\x0c' | '\x0b' | ' ' => 2,
        '!' => 3,
        '"' | '\'' | '`' => 4,
        '$' => 5,
        '%' => 6,
        '&' => 7,
        '(' ... '@' => 8 + (c as u8 - '(' as u8),
        'A' ... 'I' => sym_end + (c as u8 - 'A' as u8),
        'J' | 'K' => sym_end + letters,
        'L' ... 'P' => sym_end + (c as u8 - 'A' as u8) - 2,
        'Q' => sym_end + letters,
        'R' ... 'W' => sym_end + (c as u8 - 'A' as u8) - 3,
        'X' | 'Z' => sym_end + letters,
        'Y' => sym_end + letters - 1,
        '[' => 55,
        '\\' => 56,
        ']'=> 57,
        '^' | '~' | '#' => 58,
        '_' => 59,
        '{' ... '}' => 60 + (c as u8 - '{' as u8),
        _ => 0,
    }
}

fn explain(wut: u8) -> char {
    match wut {
        0 => 'X',
        1 => 'N',
        2 => ' ',
        3 => '!',
        4 => '"',
        5 => '$',
        6 => '%',
        7 => '&',
        8 ... 32 => (wut - 8 + '(' as u8) as char,
        33 ... 41 => (wut - 33 + 'a' as u8) as char,
        42 ... 46 => (wut - 42 + 'l' as u8) as char,
        47 ... 52 => (wut - 47 + 'r' as u8) as char,
        53 => 'y',
        54 => 'X',
        55 => '[',
        56 => '\\',
        57 => ']',
        58 => '#',
        59 => '_',
        60 ... 62 => (wut - 60 + '{' as u8) as char,
        63 => 'U',
        _ => 'D',
    }
}

fn unpack(wut: usize) -> String {
    let mut ret = String::with_capacity(3);
    ret.push(explain((wut / 64 / 64 % 64) as u8));
    ret.push(explain((wut / 64 % 64) as u8));
    ret.push(explain((wut % 64) as u8));
    return ret;
}

fn trigrams_for<T: Iterator<Item=CharResult>>(input: T) -> Result<BitSet, String> {
    let mut line: u64 = 1;
    let mut prev: [u8; 3] = [0; 3];
    let mut ret: BitSet = BitSet::with_capacity(64 * 64 * 64);

    for (off, maybe_char) in input.enumerate() {
        let c = try!(maybe_char.map_err(|e| {
            format!("line {}: file char {}: failed: {}", line, off, e)
        }));
        if '\n' == c {
            line += 1;
        }
        if '\0' == c {
            return Err(format!("line {}: null found: not a text file", line));
        }
        prev[0] = prev[1];
        prev[1] = prev[2];
        prev[2] = simplify(c);
        let tri: usize = 64 * 64 * prev[0] as usize + 64 * prev[1] as usize + prev[2] as usize;
        ret.insert(tri);
    }
    return Ok(ret);
}

struct Mapped<'a, T: 'a> {
    file: fs::File,
    map: *mut c_void,
    data: &'a mut [T],
}

impl <'a, T: 'a> Mapped<'a, T> {
    fn fixed_len<P>(path: P, len: usize) -> io::Result<Mapped<'a, T>>
        where P: AsRef<path::Path> {
        let file = fs::OpenOptions::new().read(true).write(true).create(true).open(path)?;
        file.set_len(mem::size_of::<T>() as u64 * len as u64)?;
        let map: *mut c_void = unsafe {
            libc::mmap(0 as *mut c_void,
                       len * mem::size_of::<T>(),
                       libc::PROT_READ | libc::PROT_WRITE,
                       libc::MAP_SHARED,
                       file.as_raw_fd(),
                       0)
        };

        if libc::MAP_FAILED == map {
            return Err(io::Error::last_os_error());
        }

        let data = unsafe { slice::from_raw_parts_mut(map as *mut T, len) };
        Ok(Mapped { file, map, data })
    }

    fn remap(&mut self, len: usize) -> io::Result<()> {
        self.file.set_len(mem::size_of::<T>() as u64 * len as u64)?;
        let new_map = unsafe {
            libc::mremap(self.map,
                         self.data.len() * mem::size_of::<T>(),
                         len * mem::size_of::<T>(),
                         libc::MREMAP_MAYMOVE)
        };

        if libc::MAP_FAILED == new_map {
            return Err(io::Error::last_os_error());
        }

        self.data = unsafe { slice::from_raw_parts_mut(new_map as *mut T, len) };
        self.map = new_map;
        Ok(())
    }
}

impl <'a, T: 'a> Drop for Mapped<'a, T> {
    fn drop(&mut self) {
        unsafe {
            assert_eq!(0, libc::munmap(self.map, self.data.len() * mem::size_of::<T>()));
        }
    }
}

struct Index<'a> {
    idx: Mapped<'a, u32>,
    pages: Mapped<'a, u64>,
    free_page: usize,
    page_size: usize,
}

impl<'a> Index<'a> {
    fn new() -> io::Result<Index<'a>> {
        let idx: Mapped<u32> = Mapped::fixed_len("idx", TRI_MAX).unwrap();

        let page_size: usize = 1024;

        let pages_len: usize = match fs::metadata("pages") {
            Ok(m) => {
                let proposed: u64 = m.len() / mem::size_of::<u64>() as u64;
                assert!(proposed < usize::max_value() as u64);
                proposed as usize
            },
            Err(e) => if e.kind() == io::ErrorKind::NotFound {
                2 * page_size
            } else {
                panic!("couldn't get info on pages file: {}", e)
            }
        };

        let pages: Mapped<u64> = Mapped::fixed_len("pages", pages_len)?;
        let mut free_page: usize = pages.data.len() / page_size;

        loop {
            if 0 != pages.data[(free_page - 1) * page_size] {
                break;
            }
            if 1 == free_page {
                break;
            }
            free_page -= 1;
        }

        Ok(Index { idx, pages, free_page, page_size })
    }

    fn page_for(&mut self, trigram: u32) -> io::Result<usize> {
        assert!(trigram < TRI_MAX as u32);

        let page = self.idx.data[trigram as usize] as usize;
        if 0 != page {
            return Ok(page);
        }

        self.idx.data[trigram as usize] = self.free_page as u32;
        self.free_page += 1;
        if self.free_page > self.pages.data.len() / self.page_size {
            let old_len = self.pages.data.len();
            self.pages.remap(old_len + 100 * self.page_size)?;
        }
        Ok(self.free_page)
    }

    fn append(&mut self, trigram: u32, document: u64) -> io::Result<()> {
        let page = self.page_for(trigram)?;
        let header_loc = page * self.page_size;
        let header = self.pages.data[header_loc];
        assert!(header < self.page_size as u64);
        self.pages.data[header_loc] += 1;
        self.pages.data[page * self.page_size + 1 + header as usize] = document;
        Ok(())
    }

    fn append_trigrams(&mut self, trigrams: BitSet, document: u64) -> io::Result<()> {
        for found in trigrams.iter() {
            self.append(found as u32, document)?;
        }
        Ok(())
    }
}

fn eat_chunk(fh: &mut fs::File) -> io::Result<BitSet> {
    let end = fh.read_u64::<BigEndian>()?;
    let extra_len = fh.read_u64::<BigEndian>()?;
    fh.seek(io::SeekFrom::Current(extra_len as i64))?;
    assert!(end < std::usize::MAX as u64);
    let range = fh.bytes().take((end - extra_len) as usize);
    let exploding = range.map(|x| x.unwrap());
    let decoder = std::char::decode_utf8(exploding);
    let errors = decoder.map(|x| x.map_err(|_| io::CharsError::NotUtf8));
    trigrams_for(errors).map_err(|msg| io::Error::new(io::ErrorKind::Other, msg))
}

fn main() {
    let mut from: String = "".to_string();
    let mut simple = false;
    let mut addendum: u64 = 0;
    {
        let mut ap = argparse::ArgumentParser::new();
        ap.set_description("totally not a load of tools glued together");
        ap.refer(&mut from)
                .required()
                .add_option(&["-f", "--input-file"], Store,
                            "pack file to read");
        ap.refer(&mut addendum)
                .add_option(&["-i", "--addendum"], Store,
                            "number to add to file offset");
        ap.refer(&mut simple)
                .add_option(&["--simple"], StoreTrue,
                            "not a pack, just a normal decompressed file");
        ap.parse_args_or_exit();
    }

    let mut idx = Index::new().unwrap();

    let mut fh = fs::File::open(from).expect("input file must exist and be readable");
    if simple {
        let trigrams = trigrams_for(fh.chars()).expect("trigramming must work");
        idx.append_trigrams(trigrams, addendum).unwrap();
        return;
    }

    let mut real_offset = fh.seek(io::SeekFrom::Start(16)).unwrap();
    let trigrams = eat_chunk(&mut fh).unwrap();
    idx.append_trigrams(trigrams, real_offset + addendum).unwrap();

    unimplemented!();
}
<commit_msg>make progress into the pack stream; need to alloc new pages<commit_after>#![feature(decode_utf8)]
#![feature(io)]

extern crate argparse;
extern crate bit_set;
extern crate byteorder;
extern crate compress;
extern crate libc;

use std::fs;
use std::io;
use std::mem;
use std::path;
use std::slice;

use argparse::{Store, StoreTrue};

use bit_set::BitSet;

use byteorder::{BigEndian, ReadBytesExt};

use compress::lz4;

use libc::c_void;

//magic:
use std::io::Read;
use std::io::Seek;
use std::os::unix::io::AsRawFd;

type CharResult = Result<char, io::CharsError>;

static TRI_MAX: usize = 64 * 64 * 64;

fn simplify(wut: char) -> u8 {
    let c = match wut {
        'a' ... 'z' => (wut as u8 - 'a' as u8 + 'A' as u8) as char,
        _ => wut,
    };

    if c > 128 as char {
        if c.is_whitespace() {
            return 2;
        }
        if c.is_control() {
            return 0;
        }
        return 63;
    }

    let sym_end = 33u8;
    let letters = 21u8;

    match c {
        '\r' | '\n' => 1,
        '\t' | '\x0c' | '\x0b' | ' ' => 2,
        '!' => 3,
        '"' | '\'' | '`' => 4,
        '$' => 5,
        '%' => 6,
        '&' => 7,
        '(' ... '@' => 8 + (c as u8 - '(' as u8),
        'A' ... 'I' => sym_end + (c as u8 - 'A' as u8),
        'J' | 'K' => sym_end + letters,
        'L' ... 'P' => sym_end + (c as u8 - 'A' as u8) - 2,
        'Q' => sym_end + letters,
        'R' ... 'W' => sym_end + (c as u8 - 'A' as u8) - 3,
        'X' | 'Z' => sym_end + letters,
        'Y' => sym_end + letters - 1,
        '[' => 55,
        '\\' => 56,
        ']'=> 57,
        '^' | '~' | '#' => 58,
        '_' => 59,
        '{' ... '}' => 60 + (c as u8 - '{' as u8),
        _ => 0,
    }
}

fn explain(wut: u8) -> char {
    match wut {
        0 => 'X',
        1 => 'N',
        2 => ' ',
        3 => '!',
        4 => '"',
        5 => '$',
        6 => '%',
        7 => '&',
        8 ... 32 => (wut - 8 + '(' as u8) as char,
        33 ... 41 => (wut - 33 + 'a' as u8) as char,
        42 ... 46 => (wut - 42 + 'l' as u8) as char,
        47 ... 52 => (wut - 47 + 'r' as u8) as char,
        53 => 'y',
        54 => 'X',
        55 => '[',
        56 => '\\',
        57 => ']',
        58 => '#',
        59 => '_',
        60 ... 62 => (wut - 60 + '{' as u8) as char,
        63 => 'U',
        _ => 'D',
    }
}

fn unpack(wut: usize) -> String {
    let mut ret = String::with_capacity(3);
    ret.push(explain((wut / 64 / 64 % 64) as u8));
    ret.push(explain((wut / 64 % 64) as u8));
    ret.push(explain((wut % 64) as u8));
    return ret;
}

fn trigrams_for<T: Iterator<Item=CharResult>>(input: T) -> Result<BitSet, String> {
    let mut line: u64 = 1;
    let mut prev: [u8; 3] = [0; 3];
    let mut ret: BitSet = BitSet::with_capacity(64 * 64 * 64);

    for (off, maybe_char) in input.enumerate() {
        let c = try!(maybe_char.map_err(|e| {
            format!("line {}: file char {}: failed: {}", line, off, e)
        }));
        if '\n' == c {
            line += 1;
        }
        if '\0' == c {
            return Err(format!("line {}: null found: not a text file", line));
        }
        prev[0] = prev[1];
        prev[1] = prev[2];
        prev[2] = simplify(c);
        let tri: usize = 64 * 64 * prev[0] as usize + 64 * prev[1] as usize + prev[2] as usize;
        ret.insert(tri);
    }
    return Ok(ret);
}

struct Mapped<'a, T: 'a> {
    file: fs::File,
    map: *mut c_void,
    data: &'a mut [T],
}

impl <'a, T: 'a> Mapped<'a, T> {
    fn fixed_len<P>(path: P, len: usize) -> io::Result<Mapped<'a, T>>
        where P: AsRef<path::Path> {
        let file = fs::OpenOptions::new().read(true).write(true).create(true).open(path)?;
        file.set_len(mem::size_of::<T>() as u64 * len as u64)?;
        let map: *mut c_void = unsafe {
            libc::mmap(0 as *mut c_void,
                       len * mem::size_of::<T>(),
                       libc::PROT_READ | libc::PROT_WRITE,
                       libc::MAP_SHARED,
                       file.as_raw_fd(),
                       0)
        };

        if libc::MAP_FAILED == map {
            return Err(io::Error::last_os_error());
        }

        let data = unsafe { slice::from_raw_parts_mut(map as *mut T, len) };
        Ok(Mapped { file, map, data })
    }

    fn remap(&mut self, len: usize) -> io::Result<()> {
        self.file.set_len(mem::size_of::<T>() as u64 * len as u64)?;
        let new_map = unsafe {
            libc::mremap(self.map,
                         self.data.len() * mem::size_of::<T>(),
                         len * mem::size_of::<T>(),
                         libc::MREMAP_MAYMOVE)
        };

        if libc::MAP_FAILED == new_map {
            return Err(io::Error::last_os_error());
        }

        self.data = unsafe { slice::from_raw_parts_mut(new_map as *mut T, len) };
        self.map = new_map;
        Ok(())
    }
}

impl <'a, T: 'a> Drop for Mapped<'a, T> {
    fn drop(&mut self) {
        unsafe {
            assert_eq!(0, libc::munmap(self.map, self.data.len() * mem::size_of::<T>()));
        }
    }
}

struct Index<'a> {
    idx: Mapped<'a, u32>,
    pages: Mapped<'a, u64>,
    free_page: usize,
    page_size: usize,
}

impl<'a> Index<'a> {
    fn new() -> io::Result<Index<'a>> {
        let idx: Mapped<u32> = Mapped::fixed_len("idx", TRI_MAX).unwrap();

        let page_size: usize = 1024;

        let pages_len: usize = match fs::metadata("pages") {
            Ok(m) => {
                let proposed: u64 = m.len() / mem::size_of::<u64>() as u64;
                assert!(proposed < usize::max_value() as u64);
                proposed as usize
            },
            Err(e) => if e.kind() == io::ErrorKind::NotFound {
                2 * page_size
            } else {
                panic!("couldn't get info on pages file: {}", e)
            }
        };

        let pages: Mapped<u64> = Mapped::fixed_len("pages", pages_len)?;
        let mut free_page: usize = pages.data.len() / page_size;

        loop {
            if 0 != pages.data[(free_page - 1) * page_size] {
                break;
            }
            if 1 == free_page {
                break;
            }
            free_page -= 1;
        }

        Ok(Index { idx, pages, free_page, page_size })
    }

    fn page_for(&mut self, trigram: u32) -> io::Result<usize> {
        assert!(trigram < TRI_MAX as u32);

        let page = self.idx.data[trigram as usize] as usize;
        if 0 != page {
            return Ok(page);
        }

        self.idx.data[trigram as usize] = self.free_page as u32;
        self.free_page += 1;
        if self.free_page >= self.pages.data.len() / self.page_size {
            let old_len = self.pages.data.len();
            self.pages.remap(old_len + 100 * self.page_size)?;
        }
        Ok(self.free_page)
    }

    fn append(&mut self, trigram: u32, document: u64) -> io::Result<()> {
        let page = self.page_for(trigram)?;
        let header_loc = page * self.page_size;
        let header = self.pages.data[header_loc];
        assert!(header < self.page_size as u64);
        self.pages.data[header_loc] += 1;
        self.pages.data[page * self.page_size + 1 + header as usize] = document;
        Ok(())
    }

    fn append_trigrams(&mut self, trigrams: BitSet, document: u64) -> io::Result<()> {
        for found in trigrams.iter() {
            self.append(found as u32, document)?;
        }
        Ok(())
    }
}

fn round_up(x: u64) -> u64 {
    let mut ret = x;
    loop {
        if ret % 16 == 0 {
            return ret;
        }
        ret += 1;
    }
}

fn eat_chunk(mut fh: &mut fs::File) -> io::Result<BitSet> {
    let end = fh.read_u64::<BigEndian>()?;
    let extra_len = fh.read_u64::<BigEndian>()?;
    let start = fh.seek(io::SeekFrom::Current(extra_len as i64))?;
    let ret = {
        let decoder = lz4::Decoder::new(&mut fh);
        let range = decoder.bytes();
        let exploding = range.map(|x| x.unwrap());
        let decoder = std::char::decode_utf8(exploding);
        let errors = decoder.map(|x| x.map_err(|_| io::CharsError::NotUtf8));
        trigrams_for(errors).map_err(|msg| io::Error::new(io::ErrorKind::Other, msg))
    };
    let next = round_up(start + end - extra_len - 16);
    fh.seek(io::SeekFrom::Start(next))?;
    ret
}

fn main() {
    let mut from: String = "".to_string();
    let mut simple = false;
    let mut addendum: u64 = 0;
    {
        let mut ap = argparse::ArgumentParser::new();
        ap.set_description("totally not a load of tools glued together");
        ap.refer(&mut from)
                .required()
                .add_option(&["-f", "--input-file"], Store,
                            "pack file to read");
        ap.refer(&mut addendum)
                .add_option(&["-i", "--addendum"], Store,
                            "number to add to file offset");
        ap.refer(&mut simple)
                .add_option(&["--simple"], StoreTrue,
                            "not a pack, just a normal decompressed file");
        ap.parse_args_or_exit();
    }

    let mut idx = Index::new().unwrap();

    let mut fh = fs::File::open(from).expect("input file must exist and be readable");
    if simple {
        let trigrams = trigrams_for(fh.chars()).expect("trigramming must work");
        idx.append_trigrams(trigrams, addendum).unwrap();
        return;
    }

    fh.seek(io::SeekFrom::Start(16)).unwrap();
    loop {

        let document = fh.seek(io::SeekFrom::Current(0)).unwrap() + addendum;
        let trigrams = eat_chunk(&mut fh).unwrap();
        idx.append_trigrams(trigrams, document).unwrap();
    }
}
<|endoftext|>