<commit_before>use std::collections::HashMap;
use std::io::Write;

use failure::Error;
use termion::clear::CurrentLine as ClearLine;
use termion::cursor::Goto;
use termion::event::{Event, Key, MouseButton, MouseEvent};
use xrl::{Line, LineCache, Style, Update, ConfigChanges};

use super::client::Client;
use super::style::{reset_style, set_style};
use super::window::Window;

#[derive(Debug, Default)]
pub struct Cursor {
    pub line: u64,
    pub column: u64,
}

pub struct View {
    cache: LineCache,
    cursor: Cursor,
    window: Window,
    file: Option<String>,
    client: Client,
    tab_size: u16,
}

impl View {
    pub fn new(client: Client, file: Option<String>) -> View {
        View {
            client,
            cache: LineCache::default(),
            cursor: Default::default(),
            window: Window::new(),
            file,
            tab_size: 4
        }
    }

    pub fn update_cache(&mut self, update: Update) {
        info!("updating cache");
        self.cache.update(update)
    }

    pub fn set_cursor(&mut self, line: u64, column: u64) {
        self.cursor = Cursor { line, column };
        self.window.set_cursor(&self.cursor);
    }

    pub fn config_changed(&mut self, changes: ConfigChanges) {
        if let Some(tab_size) = changes.tab_size {
            self.tab_size = tab_size as u16;
        }
    }

    pub fn render<W: Write>(
        &mut self,
        w: &mut W,
        styles: &HashMap<u64, Style>,
    ) -> Result<(), Error> {
        self.update_window();
        self.render_lines(w, styles)?;
        self.render_cursor(w);
        Ok(())
    }

    pub fn resize(&mut self, height: u16) {
        self.window.resize(height);
        self.update_window();
        self.client.scroll(
            self.cache.before() + self.window.start(),
            self.cache.after() + self.window.end(),
        );
    }

    pub fn insert(&mut self, c: char) {
        self.client.insert(c)
    }

    pub fn insert_newline(&mut self) {
        self.client.insert_newline()
    }

    pub fn insert_tab(&mut self) {
        self.client.insert_tab()
    }

    pub fn save(&mut self) {
        self.client.save(self.file.as_ref().unwrap())
    }

    pub fn back(&mut self) {
        self.client.backspace()
    }

    pub fn delete(&mut self) {
        self.client.delete()
    }

    pub fn page_down(&mut self) {
        self.client.page_down()
    }

    pub fn page_up(&mut self) {
        self.client.page_up()
    }

    fn update_window(&mut self) {
        if self.cursor.line < self.cache.before() {
            error!(
                "cursor is on line {} but there are {} invalid lines in cache.",
                self.cursor.line,
                self.cache.before()
            );
            return;
        }
        let cursor_line = self.cursor.line - self.cache.before();
        let nb_lines = self.cache.lines().len() as u64;
        self.window.update(cursor_line, nb_lines);
    }

    fn get_click_location(&self, x: u64, y: u64) -> (u64, u64) {
        let lineno = x + self.cache.before() + self.window.start();
        if let Some(line) = self.cache.lines().get(x as usize) {
            if y == 0 {
                return (lineno, 0);
            }
            let mut text_len: u16 = 0;
            for (idx, c) in line.text.chars().enumerate() {
                let char_width = self.translate_char_width(text_len, c);
                text_len += char_width;
                if u64::from(text_len) >= y {
                    // If the character at idx is wider than one column,
                    // the click occurred within the character. Otherwise,
                    // the click occurred on the character at idx + 1
                    if char_width > 1 {
                        return (lineno as u64, idx as u64);
                    } else {
                        return (lineno as u64, idx as u64 + 1);
                    }
                }
            }
            return (lineno, line.text.len() as u64 + 1);
        } else {
            warn!("no line at index {} found in cache", x);
            return (x, y);
        }
    }

    fn click(&mut self, x: u64, y: u64) {
        let (line, column) = self.get_click_location(x, y);
        self.client.click(line, column);
    }

    fn drag(&mut self, x: u64, y: u64) {
        let (line, column) = self.get_click_location(x, y);
        self.client.drag(line, column);
    }

    pub fn handle_input(&mut self, event: Event) {
        match event {
            Event::Key(key) => match key {
                Key::Char(c) => match c {
                    '\n' => self.insert_newline(),
                    '\t' => self.insert_tab(),
                    _ => self.insert(c),
                }
                Key::Ctrl(c) => match c {
                    'w' => self.save(),
                    'h' => self.back(),
                    _ => error!("un-handled input ctrl+{}", c),
                },
                Key::Backspace => self.back(),
                Key::Delete => self.delete(),
                Key::Left => self.client.left(),
                Key::Right => self.client.right(),
                Key::Up => self.client.up(),
                Key::Down => self.client.down(),
                Key::Home => self.client.home(),
                Key::End => self.client.end(),
                Key::PageUp => self.page_up(),
                Key::PageDown => self.page_down(),
                k => error!("un-handled key {:?}", k),
            },
            Event::Mouse(mouse_event) => match mouse_event {
                MouseEvent::Press(press_event, y, x) => match press_event {
                    MouseButton::Left => self.click(u64::from(x) - 1, u64::from(y) - 1),
                    MouseButton::WheelUp => self.client.up(),
                    MouseButton::WheelDown => self.client.down(),
                    button => error!("un-handled button {:?}", button),
                },
                MouseEvent::Release(..) => {}
                MouseEvent::Hold(y, x) => self.drag(u64::from(x) - 1, u64::from(y) - 1),
            },
            ev => error!("un-handled event {:?}", ev),
        }
    }

    fn render_lines<W: Write>(&self, w: &mut W, styles: &HashMap<u64, Style>) -> Result<(), Error> {
        debug!("rendering lines");
        trace!("current cache\n{:?}", self.cache);

        // Get the lines that are within the displayed window
        let lines = self
            .cache
            .lines()
            .iter()
            .skip(self.window.start() as usize)
            .take(self.window.size() as usize);

        // Draw the valid lines within this range
        for (lineno, line) in lines.enumerate() {
            self.render_line(w, line, lineno, styles);
        }

        // If the number of lines is less than window height
        // render empty lines to fill the view window.
        let line_count = self.cache.lines().len() as u16;
        let win_size = self.window.size();
        if win_size > line_count {
            for num in line_count..win_size {
                self.render_line(w, &Line::default(), num as usize, styles);
            }
        }

        Ok(())
    }

    // Next tab stop, assuming 0-based indexing
    fn tab_width_at_position(&self, position: u16) -> u16 {
        self.tab_size - (position % self.tab_size)
    }

    fn render_line<W: Write>(
        &self,
        w: &mut W,
        line: &Line,
        lineno: usize,
        styles: &HashMap<u64, Style>,
    ) {
        let text = self.escape_control_and_add_styles(styles, line);
        if let Err(e) = write!(w, "{}{}{}", Goto(1, lineno as u16 + 1), ClearLine, &text) {
            error!("failed to render line: {}", e);
        }
    }

    fn escape_control_and_add_styles(&self, styles: &HashMap<u64, Style>, line: &Line) -> String {
        let mut position: u16 = 0;
        let mut text = String::with_capacity(line.text.capacity());
        for c in line.text.chars() {
            match c {
                '\x00'...'\x08' | '\x0a'...'\x1f' | '\x7f' => {
                    // Render in caret notation, i.e. '\x02' is rendered as '^B'
                    text.push('^');
                    text.push((c as u8 ^ 0x40u8) as char);
                    position += 2;
                },
                '\t' => {
                    let tab_width = self.tab_width_at_position(position);
                    text.push_str(&" ".repeat(tab_width as usize));
                    position += tab_width;
                },
                _ => {
                    text.push(c);
                    position += 1;
                },
            }
        }
        if line.styles.is_empty() {
            return text;
        }
        let mut style_sequences = self.get_style_sequences(styles, line);
        for style in style_sequences.drain(..) {
            trace!("inserting style: {:?}", style);
            if style.0 >= text.len() {
                text.push_str(&style.1);
            } else {
                text.insert_str(style.0, &style.1);
            }
        }
        trace!("styled line: {:?}", text);
        text
    }

    fn get_style_sequences(
        &self,
        styles: &HashMap<u64, Style>,
        line: &Line,
    ) -> Vec<(usize, String)> {
        let mut style_sequences: Vec<(usize, String)> = Vec::new();
        let mut prev_style_end: usize = 0;
        for style_def in &line.styles {
            let start_idx = if style_def.offset >= 0 {
                (prev_style_end + style_def.offset as usize)
            } else {
                // FIXME: does that actually work?
                (prev_style_end - ((-style_def.offset) as usize))
            };
            let end_idx = start_idx + style_def.length as usize;
            prev_style_end = end_idx;

            if let Some(style) = styles.get(&style_def.style_id) {
                let start_sequence = match set_style(style) {
                    Ok(s) => s,
                    Err(e) => {
                        error!("could not get CSI sequence to set style {:?}: {}", style, e);
                        continue;
                    }
                };
                let end_sequence = match reset_style(style) {
                    Ok(s) => s,
                    Err(e) => {
                        error!(
                            "could not get CSI sequence to reset style {:?}: {}",
                            style, e
                        );
                        continue;
                    }
                };
                style_sequences.push((start_idx, start_sequence));
                style_sequences.push((end_idx, end_sequence));
            } else {
                error!(
                    "no style ID {} found. Not applying style.",
                    style_def.style_id
                );
            };
        }
        // Note that we sort the vector in *reverse* order, so that we apply style starting from
        // the end of the line, and we don't have to worry about the indices changing.
        style_sequences.sort_by(|a, b| a.0.cmp(&b.0));
        style_sequences.reverse();
        trace!("{:?}", style_sequences);
        style_sequences
    }

    fn render_cursor<W: Write>(&self, w: &mut W) {
        info!("rendering cursor");
        if self.cache.is_empty() {
            info!("cache is empty, rendering cursor at the top left corner");
            if let Err(e) = write!(w, "{}", Goto(1, 1)) {
                error!("failed to render cursor: {}", e);
            }
            return;
        }

        if self.cursor.line < self.cache.before() {
            error!(
                "the cursor is on line {} which is marked invalid in the cache",
                self.cursor.line
            );
            return;
        }
        // Get the line that has the cursor
        let line_idx = self.cursor.line - self.cache.before();
        let line = match self.cache.lines().get(line_idx as usize) {
            Some(line) => line,
            None => {
                error!("no valid line at cursor index {}", self.cursor.line);
                return;
            }
        };

        if line_idx < self.window.start() {
            error!(
                "the line that has the cursor (nb={}, cache_idx={}) not within the displayed window ({:?})",
                self.cursor.line,
                line_idx,
                self.window
            );
            return;
        }
        // Get the line vertical offset so that we know where to draw it.
        let line_pos = line_idx - self.window.start();

        // Calculate the cursor position on the line. The trick is that we know the position within
        // the string, but characters may have various lengths. For the moment, we only handle
        // control characters and tabs. We assume control characters (0x00-0x1f, excluding 0x09 ==
        // tab) are rendered in caret notation and are thus two columns wide. Tabs are
        // variable-width, rounding up to the next tab stop. All other characters are assumed to be
        // one column wide.
        let column: u16 = line
            .text
            .chars()
            .take(self.cursor.column as usize)
            .fold(0, |acc, c| { acc + self.translate_char_width(acc, c) });

        // Draw the cursor
        let cursor_pos = Goto(column + 1, line_pos as u16 + 1);
        if let Err(e) = write!(w, "{}", cursor_pos) {
            error!("failed to render cursor: {}", e);
        }
        info!("Cursor rendered at ({}, {})", line_pos, column);
    }

    fn translate_char_width(&self, position: u16, c: char) -> u16 {
        match c {
            // Caret notation means non-tab control characters are two columns wide
            '\x00'...'\x08' | '\x0a'...'\x1f' | '\x7f' => 2,
            '\t' => self.tab_width_at_position(position),
            _ => 1
        }
    }
}
<commit_msg>render in one write (#90)<commit_after>use std::collections::HashMap;
use std::io::Write;

use failure::Error;
use termion::clear::CurrentLine as ClearLine;
use termion::cursor::Goto;
use termion::event::{Event, Key, MouseButton, MouseEvent};
use xrl::{Line, LineCache, Style, Update, ConfigChanges};

use super::client::Client;
use super::style::{reset_style, set_style};
use super::window::Window;

#[derive(Debug, Default)]
pub struct Cursor {
    pub line: u64,
    pub column: u64,
}

pub struct View {
    cache: LineCache,
    cursor: Cursor,
    window: Window,
    file: Option<String>,
    client: Client,
    tab_size: u16,
}

impl View {
    pub fn new(client: Client, file: Option<String>) -> View {
        View {
            client,
            cache: LineCache::default(),
            cursor: Default::default(),
            window: Window::new(),
            file,
            tab_size: 4
        }
    }

    pub fn update_cache(&mut self, update: Update) {
        info!("updating cache");
        self.cache.update(update)
    }

    pub fn set_cursor(&mut self, line: u64, column: u64) {
        self.cursor = Cursor { line, column };
        self.window.set_cursor(&self.cursor);
    }

    pub fn config_changed(&mut self, changes: ConfigChanges) {
        if let Some(tab_size) = changes.tab_size {
            self.tab_size = tab_size as u16;
        }
    }

    pub fn render<W: Write>(
        &mut self,
        w: &mut W,
        styles: &HashMap<u64, Style>,
    ) -> Result<(), Error> {
        self.update_window();
        self.render_lines(w, styles)?;
        self.render_cursor(w);
        Ok(())
    }

    pub fn resize(&mut self, height: u16) {
        self.window.resize(height);
        self.update_window();
        self.client.scroll(
            self.cache.before() + self.window.start(),
            self.cache.after() + self.window.end(),
        );
    }

    pub fn insert(&mut self, c: char) {
        self.client.insert(c)
    }

    pub fn insert_newline(&mut self) {
        self.client.insert_newline()
    }

    pub fn insert_tab(&mut self) {
        self.client.insert_tab()
    }

    pub fn save(&mut self) {
        self.client.save(self.file.as_ref().unwrap())
    }

    pub fn back(&mut self) {
        self.client.backspace()
    }

    pub fn delete(&mut self) {
        self.client.delete()
    }

    pub fn page_down(&mut self) {
        self.client.page_down()
    }

    pub fn page_up(&mut self) {
        self.client.page_up()
    }

    fn update_window(&mut self) {
        if self.cursor.line < self.cache.before() {
            error!(
                "cursor is on line {} but there are {} invalid lines in cache.",
                self.cursor.line,
                self.cache.before()
            );
            return;
        }
        let cursor_line = self.cursor.line - self.cache.before();
        let nb_lines = self.cache.lines().len() as u64;
        self.window.update(cursor_line, nb_lines);
    }

    fn get_click_location(&self, x: u64, y: u64) -> (u64, u64) {
        let lineno = x + self.cache.before() + self.window.start();
        if let Some(line) = self.cache.lines().get(x as usize) {
            if y == 0 {
                return (lineno, 0);
            }
            let mut text_len: u16 = 0;
            for (idx, c) in line.text.chars().enumerate() {
                let char_width = self.translate_char_width(text_len, c);
                text_len += char_width;
                if u64::from(text_len) >= y {
                    // If the character at idx is wider than one column,
                    // the click occurred within the character. Otherwise,
                    // the click occurred on the character at idx + 1
                    if char_width > 1 {
                        return (lineno as u64, idx as u64);
                    } else {
                        return (lineno as u64, idx as u64 + 1);
                    }
                }
            }
            return (lineno, line.text.len() as u64 + 1);
        } else {
            warn!("no line at index {} found in cache", x);
            return (x, y);
        }
    }

    fn click(&mut self, x: u64, y: u64) {
        let (line, column) = self.get_click_location(x, y);
        self.client.click(line, column);
    }

    fn drag(&mut self, x: u64, y: u64) {
        let (line, column) = self.get_click_location(x, y);
        self.client.drag(line, column);
    }

    pub fn handle_input(&mut self, event: Event) {
        match event {
            Event::Key(key) => match key {
                Key::Char(c) => match c {
                    '\n' => self.insert_newline(),
                    '\t' => self.insert_tab(),
                    _ => self.insert(c),
                }
                Key::Ctrl(c) => match c {
                    'w' => self.save(),
                    'h' => self.back(),
                    _ => error!("un-handled input ctrl+{}", c),
                },
                Key::Backspace => self.back(),
                Key::Delete => self.delete(),
                Key::Left => self.client.left(),
                Key::Right => self.client.right(),
                Key::Up => self.client.up(),
                Key::Down => self.client.down(),
                Key::Home => self.client.home(),
                Key::End => self.client.end(),
                Key::PageUp => self.page_up(),
                Key::PageDown => self.page_down(),
                k => error!("un-handled key {:?}", k),
            },
            Event::Mouse(mouse_event) => match mouse_event {
                MouseEvent::Press(press_event, y, x) => match press_event {
                    MouseButton::Left => self.click(u64::from(x) - 1, u64::from(y) - 1),
                    MouseButton::WheelUp => self.client.up(),
                    MouseButton::WheelDown => self.client.down(),
                    button => error!("un-handled button {:?}", button),
                },
                MouseEvent::Release(..) => {}
                MouseEvent::Hold(y, x) => self.drag(u64::from(x) - 1, u64::from(y) - 1),
            },
            ev => error!("un-handled event {:?}", ev),
        }
    }

    fn render_lines<W: Write>(&self, w: &mut W, styles: &HashMap<u64, Style>) -> Result<(), Error> {
        debug!("rendering lines");
        trace!("current cache\n{:?}", self.cache);

        // Get the lines that are within the displayed window
        let lines = self
            .cache
            .lines()
            .iter()
            .skip(self.window.start() as usize)
            .take(self.window.size() as usize);

        // Draw the valid lines within this range
        let mut line_strings = String::new();
        for (lineno, line) in lines.enumerate() {
            line_strings.push_str(&self.render_line_str(line, lineno, styles));
        }

        // If the number of lines is less than window height
        // render empty lines to fill the view window.
        let line_count = self.cache.lines().len() as u16;
        let win_size = self.window.size();
        if win_size > line_count {
            for num in line_count..win_size {
                line_strings.push_str(&self.render_line_str(
                    &Line::default(),
                    num as usize,
                    styles,
                ));
            }
        }
        w.write(line_strings.as_bytes())?;

        Ok(())
    }

    // Next tab stop, assuming 0-based indexing
    fn tab_width_at_position(&self, position: u16) -> u16 {
        self.tab_size - (position % self.tab_size)
    }

    fn render_line_str(&self, line: &Line, lineno: usize, styles: &HashMap<u64, Style>) -> String {
        let text = self.escape_control_and_add_styles(styles, line);
        format!("{}{}{}", Goto(1, lineno as u16 + 1), ClearLine, &text)
    }

    fn escape_control_and_add_styles(&self, styles: &HashMap<u64, Style>, line: &Line) -> String {
        let mut position: u16 = 0;
        let mut text = String::with_capacity(line.text.capacity());
        for c in line.text.chars() {
            match c {
                '\x00'...'\x08' | '\x0a'...'\x1f' | '\x7f' => {
                    // Render in caret notation, i.e. '\x02' is rendered as '^B'
                    text.push('^');
                    text.push((c as u8 ^ 0x40u8) as char);
                    position += 2;
                },
                '\t' => {
                    let tab_width = self.tab_width_at_position(position);
                    text.push_str(&" ".repeat(tab_width as usize));
                    position += tab_width;
                },
                _ => {
                    text.push(c);
                    position += 1;
                },
            }
        }
        if line.styles.is_empty() {
            return text;
        }
        let mut style_sequences = self.get_style_sequences(styles, line);
        for style in style_sequences.drain(..) {
            trace!("inserting style: {:?}", style);
            if style.0 >= text.len() {
                text.push_str(&style.1);
            } else {
                text.insert_str(style.0, &style.1);
            }
        }
        trace!("styled line: {:?}", text);
        text
    }

    fn get_style_sequences(
        &self,
        styles: &HashMap<u64, Style>,
        line: &Line,
    ) -> Vec<(usize, String)> {
        let mut style_sequences: Vec<(usize, String)> = Vec::new();
        let mut prev_style_end: usize = 0;
        for style_def in &line.styles {
            let start_idx = if style_def.offset >= 0 {
                (prev_style_end + style_def.offset as usize)
            } else {
                // FIXME: does that actually work?
                (prev_style_end - ((-style_def.offset) as usize))
            };
            let end_idx = start_idx + style_def.length as usize;
            prev_style_end = end_idx;

            if let Some(style) = styles.get(&style_def.style_id) {
                let start_sequence = match set_style(style) {
                    Ok(s) => s,
                    Err(e) => {
                        error!("could not get CSI sequence to set style {:?}: {}", style, e);
                        continue;
                    }
                };
                let end_sequence = match reset_style(style) {
                    Ok(s) => s,
                    Err(e) => {
                        error!(
                            "could not get CSI sequence to reset style {:?}: {}",
                            style, e
                        );
                        continue;
                    }
                };
                style_sequences.push((start_idx, start_sequence));
                style_sequences.push((end_idx, end_sequence));
            } else {
                error!(
                    "no style ID {} found. Not applying style.",
                    style_def.style_id
                );
            };
        }
        // Note that we sort the vector in *reverse* order, so that we apply style starting from
        // the end of the line, and we don't have to worry about the indices changing.
        style_sequences.sort_by(|a, b| a.0.cmp(&b.0));
        style_sequences.reverse();
        trace!("{:?}", style_sequences);
        style_sequences
    }

    fn render_cursor<W: Write>(&self, w: &mut W) {
        info!("rendering cursor");
        if self.cache.is_empty() {
            info!("cache is empty, rendering cursor at the top left corner");
            if let Err(e) = write!(w, "{}", Goto(1, 1)) {
                error!("failed to render cursor: {}", e);
            }
            return;
        }

        if self.cursor.line < self.cache.before() {
            error!(
                "the cursor is on line {} which is marked invalid in the cache",
                self.cursor.line
            );
            return;
        }
        // Get the line that has the cursor
        let line_idx = self.cursor.line - self.cache.before();
        let line = match self.cache.lines().get(line_idx as usize) {
            Some(line) => line,
            None => {
                error!("no valid line at cursor index {}", self.cursor.line);
                return;
            }
        };

        if line_idx < self.window.start() {
            error!(
                "the line that has the cursor (nb={}, cache_idx={}) not within the displayed window ({:?})",
                self.cursor.line,
                line_idx,
                self.window
            );
            return;
        }
        // Get the line vertical offset so that we know where to draw it.
        let line_pos = line_idx - self.window.start();

        // Calculate the cursor position on the line. The trick is that we know the position within
        // the string, but characters may have various lengths. For the moment, we only handle
        // control characters and tabs. We assume control characters (0x00-0x1f, excluding 0x09 ==
        // tab) are rendered in caret notation and are thus two columns wide. Tabs are
        // variable-width, rounding up to the next tab stop. All other characters are assumed to be
        // one column wide.
        let column: u16 = line
            .text
            .chars()
            .take(self.cursor.column as usize)
            .fold(0, |acc, c| { acc + self.translate_char_width(acc, c) });

        // Draw the cursor
        let cursor_pos = Goto(column + 1, line_pos as u16 + 1);
        if let Err(e) = write!(w, "{}", cursor_pos) {
            error!("failed to render cursor: {}", e);
        }
        info!("Cursor rendered at ({}, {})", line_pos, column);
    }

    fn translate_char_width(&self, position: u16, c: char) -> u16 {
        match c {
            // Caret notation means non-tab control characters are two columns wide
            '\x00'...'\x08' | '\x0a'...'\x1f' | '\x7f' => 2,
            '\t' => self.tab_width_at_position(position),
            _ => 1
        }
    }
}
<|endoftext|>


<commit_before>// Kevin Cantu
// Boyer-Moore string searching

use std;

// Function: findn_str_between
//
// Boyer-Moore string search, which returns
// up to `nn` byte positions of matched substrings
fn findn_str_between (haystack: str, needle: str,
                      nn: uint,
                      start: uint, end: uint) -> [uint] {

   let results = [];

   let nlen = str::len(needle);
   let hlen = str::len(haystack);

   // empty haystack
   if hlen == 0u {
      ret results;
   }

   // empty needle
   if nlen == 0u {
      vec::push(results, 0u);
      ret results;
   }

   // needle too large
   if hlen < nlen {
      ret results;
   }

   // generate the tables
   let ct = char_table(needle);
   let pt = prefix_table(needle);

   // query both
   // based on position within the needle and character in haystack
   let getShift = fn@(pos: uint, ch: u8) -> uint {
      let charShift = ct[ch as uint];
      let prefShift = pt[nlen - 1u - pos];

      if charShift > prefShift {
         ret charShift;
      } else {
         ret prefShift;
      }
   };

   // step up through the haystack
   let outerii = start;
   while outerii < end - nlen + 1u {

      // step back through needle
      let windowii = nlen;
      while 0u < windowii
            && (outerii + windowii - 1u) < hlen  // don't exceed haystack
      {
         windowii -= 1u;

         if needle[windowii] == haystack[outerii+windowii] {
            // still matching

            // full match?
            if windowii == 0u {
               vec::push(results, outerii);

               if vec::len(results) >= nn {
                  ret results;
               } else {
                  outerii += nlen;
               }
            }
         } else {
            // not matching yet
            // was partial match
            outerii += getShift(windowii, haystack[outerii+windowii]);
            break;
         }
      }
   }

   ret results;
}

// compute the table used to choose a shift based on
// an unmatched character's possible position within the search string
// (a.k.a. the bad-character table)
fn char_table (needle: str) -> [uint] {
   let len = str::len(needle);
   let mm  = vec::init_elt_mut(255u, len);

   let jj = len - 1u; // drop the last byte

   assert jj >= 0u;
   assert jj < str::len(needle);

   // from last-1 to first
   while jj > 0u {
      jj -= 1u;

      let key = needle[jj] as uint;

      // if we haven't set it yet, set it now
      // (besides default)
      if mm[key] == len {
         mm[key] = len - 1u - jj;
      }
   }

   ret vec::from_mut(mm);
}

// compute the table used to choose a shift based on
// a partially matched suffix of the search string
// (a.k.a. the good-suffix table)
fn prefix_table (needle: str) -> [uint] {
   let needle_ = str::bytes(needle);

   let len   = vec::len(needle_);
   assert 0u < len;

   // initialize len chars to len
   let mm  = vec::init_elt_mut(len, len);

   // step to larger suffixes
   let sii = 0u;
   while sii < len {

      // tail of the needle we seek
      let suffix      = vec::slice(needle_, len - sii,      len);
      let suffix_plus = vec::slice(needle_, len - sii - 1u, len);
      let slen = vec::len(suffix);

      // step to smaller prefixes
      let pii = len - 1u;
      while pii > 0u {

         // a prefix of the needle that might be matched by
         // a partial match of a suffix
         // (which we might want to jump to)
         let prefix = vec::slice(needle_, 0u, pii);
         let plen = vec::len(prefix);

         // suffix might be fully matched
         let is_suffix_matched =
            vec::ends_with(prefix, suffix)
            && !vec::ends_with(prefix, suffix_plus)
            &&  slen < plen;

         // prefix is bigger than suffix, only tail can match
         let is_suffix_partially_matched =
            plen <= slen
            && vec::ends_with(suffix, prefix);

         if is_suffix_matched || is_suffix_partially_matched {
            // if we haven't set it yet, set it now
            // (besides default)
            if mm[sii] == len {
               mm[sii] = len-pii;
            }
         }

         pii -= 1u;
      }

      // no matching prefix
      // if we haven't set it yet, set it now
      // (besides default)
      if mm[sii] == len {
         mm[sii] = len-pii;
      }

      sii += 1u;
   }

   ret vec::from_mut(mm);
}

#[test]
fn test_char_table () {
   let ct = char_table("ANPANMAN");
   assert 1u == ct['A' as uint];
   assert 2u == ct['M' as uint];
   assert 3u == ct['N' as uint];
   assert 5u == ct['P' as uint];
   assert str::len("ANPANMAN") == ct['z' as uint];
}

#[test]
fn test_char_table_utf8() {
   let ct = char_table("ะเ"); //e0b8b0 e0b980
   assert 2u == ct[0x_e0_u];
   assert 4u == ct[0x_b8_u];
   assert 3u == ct[0x_b0_u];
   assert 2u == ct[0x_e0_u];
   assert 1u == ct[0x_b9_u];
   assert 6u == ct[0x_80_u];
   assert 6u == ct["ะเ"[5u]]
}

#[test]
fn test_prefix_table_ascii() {
   let pt = prefix_table("ANPANMAN");
                            //      ... 8

   assert 1u == pt[0u]; //        (n)
   assert 8u == pt[1u]; //       (a)n
   assert 3u == pt[2u]; //      (m)an
   assert 6u == pt[3u]; //     (n)man
   assert 6u == pt[4u]; //    (a)nman
   assert 6u == pt[5u]; //   (p)anman
   assert 6u == pt[6u]; //  (n)panman
   assert 6u == pt[7u]; // (a)npanman
   //assert 0u == pt.get(8u); // fail
}

#[test]
fn test_prefix_table_utf8() {
   let pt = prefix_table("ประเ");

   assert  1u == pt[0u];
   assert 12u == pt[3u];
   assert 12u == pt[6u];
   assert 12u == pt[9u];
}

fn findn_str(haystack: str, needle: str, nn: uint) -> [uint] {
   findn_str_between(haystack, needle, nn, 0u, str::len(haystack))
}

fn find_str_(haystack: str, needle: str) -> option<uint> {
   let found = findn_str(haystack, needle, 1u);

   alt vec::len(found) {
      0u { option::none }
      xx { option::some(found[0u]) }
   }
}

#[test]
fn test_findn_str() {
   assert [] == findn_str("banana", "apple pie", 1u);
   assert (findn_str("abcxxxxxx", "abc", 1u) == [0u]);
   assert (findn_str("xxxabcxxx", "abc", 1u) == [3u]);
   assert (findn_str("xxxxxxabc", "abc", 1u) == [6u]);
   assert (findn_str("xxxabcabc", "abc", 1u) == [3u]);
   assert (findn_str("xxxabcabc", "abc", 5u) == [3u, 6u]);
   assert (findn_str("xxxabcxabc", "abc", 5u) == [3u, 7u]);
   assert (findn_str("xxxabcxxabc", "abc", 5u) == [3u, 8u]);
}

#[test]
fn test_find_strX_ascii() {
  assert (find_str_("banana", "apple pie") == option::none);
  assert (find_str_("", "") == option::none);
  assert (find_str_("abcxxxxxx", "abc") == option::some(0u));
  assert (find_str_("xxxabcxxx", "abc") == option::some(3u));
  assert (find_str_("xxxxxxabc", "abc") == option::some(6u));
}

#[test]
fn test_find_strX_utf8() {
  let data = "ประเทศไทย中华Việt Nam";
  assert (find_str_(data, "ไท华") == option::none);
  assert (find_str_(data, "")     == option::some( 0u));
  assert (find_str_(data, "ประเ") == option::some( 0u));
  assert (find_str_(data, "ระ") == option::some(3u));
  assert (find_str_(data, "ย中华") == option::some(24u));
}

#[test]
fn test_find_strX_utf8_B() {
// PENDING
   let data = "ประเทศไทย中华Việt Nam";
   assert (find_str_(data, "ะเ")   == option::some( 6u));
   assert (find_str_(data, "ศไทย中华") == option::some(15u));
   assert (find_str_(data, "ไทย中华") == option::some(18u));
   assert (find_str_(data, "中华") == option::some(27u));
}

<commit_msg>better yet...<commit_after>// Kevin Cantu
// Boyer-Moore string searching

use std;

// Function: findn_str_between
//
// Boyer-Moore string search, which returns
// up to `nn` byte positions of matched substrings
fn findn_str_between (haystack: str, needle: str,
                      nn: uint,
                      start: uint, end: uint) -> [uint] {

   let results = [];

   let nlen = str::len(needle);
   let hlen = str::len(haystack);

   // empty haystack
   if hlen == 0u {
      ret results;
   }

   // empty needle
   if nlen == 0u {
      vec::push(results, 0u);
      ret results;
   }

   // needle too large
   if hlen < nlen {
      ret results;
   }

   // generate the tables
   let ct = char_table(needle);
   let pt = prefix_table(needle);

   // query both
   // based on position within the needle and character in haystack
   let getShift = fn@(pos: uint, ch: u8) -> uint {
      let charShift = ct[ch as uint];
      let prefShift = pt[nlen - 1u - pos];

      if charShift > prefShift {
         ret charShift;
      } else {
         ret prefShift;
      }
   };

   // step up through the haystack
   let outerii = start;
   while outerii < end - nlen + 1u {

      // step back through needle
      let windowii = nlen;
      while 0u < windowii
            && (outerii + windowii - 1u) < hlen  // don't exceed haystack
      {
         windowii -= 1u;

         if needle[windowii] == haystack[outerii+windowii] {
            // still matching

            if windowii == 0u {
               // matched
               vec::push(results, outerii);

               if vec::len(results) >= nn {
                  ret results;
               } else {
                  outerii += nlen;
               }
            }
         } else {
            // not matching yet or was partial match
            outerii += getShift(windowii, haystack[outerii+windowii]);
            break;
         }
      }
   }

   ret results;
}

// compute the table used to choose a shift based on
// an unmatched character's possible position within the search string
// (a.k.a. the bad-character table)
fn char_table (needle: str) -> [uint] {
   let len = str::len(needle);
   let mm  = vec::init_elt_mut(255u, len);

   let jj = len - 1u; // drop the last byte

   assert jj >= 0u;
   assert jj < str::len(needle);

   // from last-1 to first
   while jj > 0u {
      jj -= 1u;

      let key = needle[jj] as uint;

      // if we haven't set it yet, set it now
      // (besides default)
      if mm[key] == len {
         mm[key] = len - 1u - jj;
      }
   }

   ret vec::from_mut(mm);
}

// compute the table used to choose a shift based on
// a partially matched suffix of the search string
// (a.k.a. the good-suffix table)
fn prefix_table (needle: str) -> [uint] {
   let needle_ = str::bytes(needle);

   let len   = vec::len(needle_);
   assert 0u < len;

   // initialize len chars to len
   let mm  = vec::init_elt_mut(len, len);

   // step to larger suffixes
   let sii = 0u;
   while sii < len {

      // tail of the needle we seek
      let suffix      = vec::slice(needle_, len - sii,      len);
      let suffix_plus = vec::slice(needle_, len - sii - 1u, len);
      let slen = vec::len(suffix);

      // step to smaller prefixes
      let pii = len - 1u;
      while pii > 0u {

         // a prefix of the needle that might be matched by
         // a partial match of a suffix
         // (which we might want to jump to)
         let prefix = vec::slice(needle_, 0u, pii);
         let plen = vec::len(prefix);

         // suffix might be fully matched
         let is_suffix_matched =
            vec::ends_with(prefix, suffix)
            && !vec::ends_with(prefix, suffix_plus)
            &&  slen < plen;

         // prefix is bigger than suffix, only tail can match
         let is_suffix_partially_matched =
            plen <= slen
            && vec::ends_with(suffix, prefix);

         if is_suffix_matched || is_suffix_partially_matched {
            // if we haven't set it yet, set it now
            // (besides default)
            if mm[sii] == len {
               mm[sii] = len-pii;
            }
         }

         pii -= 1u;
      }

      // no matching prefix
      // if we haven't set it yet, set it now
      // (besides default)
      if mm[sii] == len {
         mm[sii] = len-pii;
      }

      sii += 1u;
   }

   ret vec::from_mut(mm);
}

#[test]
fn test_char_table () {
   let ct = char_table("ANPANMAN");
   assert 1u == ct['A' as uint];
   assert 2u == ct['M' as uint];
   assert 3u == ct['N' as uint];
   assert 5u == ct['P' as uint];
   assert str::len("ANPANMAN") == ct['z' as uint];
}

#[test]
fn test_char_table_utf8() {
   let ct = char_table("ะเ"); //e0b8b0 e0b980
   assert 2u == ct[0x_e0_u];
   assert 4u == ct[0x_b8_u];
   assert 3u == ct[0x_b0_u];
   assert 2u == ct[0x_e0_u];
   assert 1u == ct[0x_b9_u];
   assert 6u == ct[0x_80_u];
   assert 6u == ct["ะเ"[5u]]
}

#[test]
fn test_prefix_table_ascii() {
   let pt = prefix_table("ANPANMAN");
                            //      ... 8

   assert 1u == pt[0u]; //        (n)
   assert 8u == pt[1u]; //       (a)n
   assert 3u == pt[2u]; //      (m)an
   assert 6u == pt[3u]; //     (n)man
   assert 6u == pt[4u]; //    (a)nman
   assert 6u == pt[5u]; //   (p)anman
   assert 6u == pt[6u]; //  (n)panman
   assert 6u == pt[7u]; // (a)npanman
   //assert 0u == pt.get(8u); // fail
}

#[test]
fn test_prefix_table_utf8() {
   let pt = prefix_table("ประเ");

   assert  1u == pt[0u];
   assert 12u == pt[3u];
   assert 12u == pt[6u];
   assert 12u == pt[9u];
}

fn findn_str(haystack: str, needle: str, nn: uint) -> [uint] {
   findn_str_between(haystack, needle, nn, 0u, str::len(haystack))
}

fn find_str_(haystack: str, needle: str) -> option<uint> {
   let found = findn_str(haystack, needle, 1u);

   alt vec::len(found) {
      0u { option::none }
      xx { option::some(found[0u]) }
   }
}

#[test]
fn test_findn_str() {
   assert [] == findn_str("banana", "apple pie", 1u);
   assert (findn_str("abcxxxxxx", "abc", 1u) == [0u]);
   assert (findn_str("xxxabcxxx", "abc", 1u) == [3u]);
   assert (findn_str("xxxxxxabc", "abc", 1u) == [6u]);
   assert (findn_str("xxxabcabc", "abc", 1u) == [3u]);
   assert (findn_str("xxxabcabc", "abc", 5u) == [3u, 6u]);
   assert (findn_str("xxxabcxabc", "abc", 5u) == [3u, 7u]);
   assert (findn_str("xxxabcxxabc", "abc", 5u) == [3u, 8u]);
}

#[test]
fn test_find_strX_ascii() {
  assert (find_str_("banana", "apple pie") == option::none);
  assert (find_str_("", "") == option::none);
  assert (find_str_("abcxxxxxx", "abc") == option::some(0u));
  assert (find_str_("xxxabcxxx", "abc") == option::some(3u));
  assert (find_str_("xxxxxxabc", "abc") == option::some(6u));
}

#[test]
fn test_find_strX_utf8() {
  let data = "ประเทศไทย中华Việt Nam";
  assert (find_str_(data, "ไท华") == option::none);
  assert (find_str_(data, "")     == option::some( 0u));
  assert (find_str_(data, "ประเ") == option::some( 0u));
  assert (find_str_(data, "ระ") == option::some(3u));
  assert (find_str_(data, "ย中华") == option::some(24u));
}

#[test]
fn test_find_strX_utf8_B() {
// PENDING
   let data = "ประเทศไทย中华Việt Nam";
   assert (find_str_(data, "ะเ")   == option::some( 6u));
   assert (find_str_(data, "ศไทย中华") == option::some(15u));
   assert (find_str_(data, "ไทย中华") == option::some(18u));
   assert (find_str_(data, "中华") == option::some(27u));
}

<|endoftext|>


<commit_before>use bson::oid::ObjectId;
use rustc_serialize::hex::ToHex;
use rustc_serialize::json;

#[test]
fn deserialize() {
    let bytes: [u8; 12] = [
        0xDEu8,
        0xADu8,
        0xBEu8,
        0xEFu8,  // timestamp is 3735928559
        0xEFu8,
        0xCDu8,
        0xABu8,  // machine_id is 11259375
        0xFAu8,
        0x29u8,  // process_id is 10746
        0x11u8,
        0x22u8,
        0x33u8,  // increment is 1122867
        ];

    let oid = ObjectId::with_bytes(bytes);
    assert_eq!(3735928559 as u32, oid.timestamp());
    assert_eq!(11259375 as u32, oid.machine_id());
    assert_eq!(10746 as u16, oid.process_id());
    assert_eq!(1122867 as u32, oid.counter());
}

#[test]
fn timestamp() {
    let time: u32 = 2000000;
    let oid = ObjectId::with_timestamp(time);
    let timestamp = oid.timestamp();
    assert_eq!(time, timestamp);
}

#[test]
fn timestamp_is_big_endian() {
    let time: u32 = 3857379;
    let oid = ObjectId::with_timestamp(time);
    assert_eq!(0x00u8, oid.bytes()[0]);
    assert_eq!(0x3Au8, oid.bytes()[1]);
    assert_eq!(0xDBu8, oid.bytes()[2]);
    assert_eq!(0xE3u8, oid.bytes()[3]);
}

#[test]
fn string_oid() {
    let s = "123456789012123456789012";
    let oid_res = ObjectId::with_string(s);
    assert!(oid_res.is_ok());
    let actual_s = oid_res.unwrap().bytes().to_hex();
    assert_eq!(s.to_owned(), actual_s);
}

#[test]
fn byte_string_oid() {
    let s = "541b1a00e8a23afa832b218e";
    let oid_res = ObjectId::with_string(s);
    assert!(oid_res.is_ok());
    let oid = oid_res.unwrap();
    let bytes: [u8; 12] = [0x54u8, 0x1Bu8, 0x1Au8, 0x00u8,
                           0xE8u8, 0xA2u8, 0x3Au8, 0xFAu8,
                           0x83u8, 0x2Bu8, 0x21u8, 0x8Eu8];

    assert_eq!(bytes, oid.bytes());
    assert_eq!(s, oid.to_string());
}

#[test]
fn oid_equals() {
    let oid_res = ObjectId::new();
    assert!(oid_res.is_ok());
    let oid = oid_res.unwrap();
    assert_eq!(oid, oid);
}

#[test]
fn oid_not_equals() {
    let oid1_res = ObjectId::new();
    let oid2_res = ObjectId::new();
    assert!(oid1_res.is_ok());
    assert!(oid2_res.is_ok());

    let oid1 = oid1_res.unwrap();
    let oid2 = oid2_res.unwrap();
    assert!(oid1 != oid2);
}

#[test]
fn increasing() {
    let oid1_res = ObjectId::new();
    let oid2_res = ObjectId::new();
    assert!(oid1_res.is_ok());
    assert!(oid2_res.is_ok());

    let oid1 = oid1_res.unwrap();
    let oid2 = oid2_res.unwrap();
    assert!(oid1 < oid2);
}

#[derive(RustcEncodable,RustcDecodable,PartialEq,Debug)]
struct StructWithObjectId {
    id: ObjectId
}

#[test]
fn use_in_rustc_serialize() {
    let s = "541b1a00e8a23afa832b218e";
    let s = StructWithObjectId {
        id: ObjectId::with_string(s).unwrap()
    };
    let encoded = json::encode(&s).unwrap();
    assert_eq!("{\"id\":\"541b1a00e8a23afa832b218e\"}", encoded);

    let decoded: StructWithObjectId = json::decode(&encoded).unwrap();
    assert_eq!(s, decoded);
}
<commit_msg>Remove rustc_serialize test<commit_after>use bson::oid::ObjectId;
use rustc_serialize::hex::ToHex;
use rustc_serialize::json;

#[test]
fn deserialize() {
    let bytes: [u8; 12] = [
        0xDEu8,
        0xADu8,
        0xBEu8,
        0xEFu8,  // timestamp is 3735928559
        0xEFu8,
        0xCDu8,
        0xABu8,  // machine_id is 11259375
        0xFAu8,
        0x29u8,  // process_id is 10746
        0x11u8,
        0x22u8,
        0x33u8,  // increment is 1122867
        ];

    let oid = ObjectId::with_bytes(bytes);
    assert_eq!(3735928559 as u32, oid.timestamp());
    assert_eq!(11259375 as u32, oid.machine_id());
    assert_eq!(10746 as u16, oid.process_id());
    assert_eq!(1122867 as u32, oid.counter());
}

#[test]
fn timestamp() {
    let time: u32 = 2000000;
    let oid = ObjectId::with_timestamp(time);
    let timestamp = oid.timestamp();
    assert_eq!(time, timestamp);
}

#[test]
fn timestamp_is_big_endian() {
    let time: u32 = 3857379;
    let oid = ObjectId::with_timestamp(time);
    assert_eq!(0x00u8, oid.bytes()[0]);
    assert_eq!(0x3Au8, oid.bytes()[1]);
    assert_eq!(0xDBu8, oid.bytes()[2]);
    assert_eq!(0xE3u8, oid.bytes()[3]);
}

#[test]
fn string_oid() {
    let s = "123456789012123456789012";
    let oid_res = ObjectId::with_string(s);
    assert!(oid_res.is_ok());
    let actual_s = oid_res.unwrap().bytes().to_hex();
    assert_eq!(s.to_owned(), actual_s);
}

#[test]
fn byte_string_oid() {
    let s = "541b1a00e8a23afa832b218e";
    let oid_res = ObjectId::with_string(s);
    assert!(oid_res.is_ok());
    let oid = oid_res.unwrap();
    let bytes: [u8; 12] = [0x54u8, 0x1Bu8, 0x1Au8, 0x00u8,
                           0xE8u8, 0xA2u8, 0x3Au8, 0xFAu8,
                           0x83u8, 0x2Bu8, 0x21u8, 0x8Eu8];

    assert_eq!(bytes, oid.bytes());
    assert_eq!(s, oid.to_string());
}

#[test]
fn oid_equals() {
    let oid_res = ObjectId::new();
    assert!(oid_res.is_ok());
    let oid = oid_res.unwrap();
    assert_eq!(oid, oid);
}

#[test]
fn oid_not_equals() {
    let oid1_res = ObjectId::new();
    let oid2_res = ObjectId::new();
    assert!(oid1_res.is_ok());
    assert!(oid2_res.is_ok());

    let oid1 = oid1_res.unwrap();
    let oid2 = oid2_res.unwrap();
    assert!(oid1 != oid2);
}

#[test]
fn increasing() {
    let oid1_res = ObjectId::new();
    let oid2_res = ObjectId::new();
    assert!(oid1_res.is_ok());
    assert!(oid2_res.is_ok());

    let oid1 = oid1_res.unwrap();
    let oid2 = oid2_res.unwrap();
    assert!(oid1 < oid2);
}
<|endoftext|>


<commit_before>extern mod std;

use std::run;
use std::str;
use std::option::*;
use std::trie::*;
use std::libc::{execvp, perror};
use std::c_str::*;
use std::vec;
use std::ptr;

fn session_is_attached(line: &str) -> bool {
    line.ends_with("(attached)")
}

fn session_number(line: &str) -> Option<uint> {
    line.split_iter(':').next().and_then(|s| from_str(s))
}

fn detached_sessions(output: ~str) -> TrieSet {
    let mut set = TrieSet::new();
    for line in output.line_iter(){
        if !session_is_attached(line) {
            match session_number(line) {
                Some(n) => { set.insert(n); }
                None => ()
            }
        }
    }
    set
}

#[fixed_stack_segment]
fn exec_program(program: &str, args: &[~str]) {
    do program.to_c_str().with_ref() |c_program| {
        // I don't much care about the ownership of the strings here
        // at this point, so let's just fail if execvp isn't working.
        unsafe {
            let mut c_args = args.map(|arg| { arg.to_c_str().unwrap() });
            c_args.push(ptr::null());
            execvp(c_program, vec::raw::to_ptr(c_args));
            perror("Running tmux failed:".to_c_str().unwrap());
        }
        fail!(fmt!("Couldn't exec %s", program));
    }
}

fn main() {
    let proc = run::process_output("tmux", &[~"list-sessions"]);
    if proc.status != 0 {
        fail!(fmt!("Tmux exited with an error status: %d", proc.status));
    }
    let output = str::from_utf8(proc.output);
    let sessions = detached_sessions(output);
    match sessions.iter().next() {
        Some(n) => {
            let session = n.to_str();
            exec_program("tmux", &[~"tmux", ~"attach-session", ~"-t", session]);
        }
        _ => { exec_program("tmux", &[~"tmux"]); }
    }
}

#[test]
fn test_session_is_attached() {
    assert!(session_is_attached("11: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65] (attached)"));
    assert!(!session_is_attached("20: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65]"));
}

#[test]
fn test_session_number_with_numbers(){
    match(session_number("11: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65] (attached)")) {
        Some(11) => (),
        Some(n) => fail!(fmt!("Should have returned 11, got %u!", n)),
        None => fail!("Should have returned something, got nothing"),
    }
}

#[test]
fn test_session_number_with_strings(){
    match(session_number("oink: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65] (attached)")) {
        Some(n) => fail!(fmt!("Should have returned None, got %u!", n)),
        None => ()
    }
}

#[test]
fn test_detached_sessions() {
    let set = detached_sessions(~"3: foo (attached)\n2: bar\n4: abz (attached)\nfoo: baz");
    assert!(set.contains(&2));

    // attached sessions:
    assert!(!set.contains(&3));
    assert!(!set.contains(&4));

    // missing session:
    assert!(!set.contains(&1));
}
<commit_msg>Use with_c_str() instead of the long form<commit_after>extern mod std;

use std::run;
use std::str;
use std::option::*;
use std::trie::*;
use std::libc::{execvp, perror};
use std::c_str::*;
use std::vec;
use std::ptr;

fn session_is_attached(line: &str) -> bool {
    line.ends_with("(attached)")
}

fn session_number(line: &str) -> Option<uint> {
    line.split_iter(':').next().and_then(|s| from_str(s))
}

fn detached_sessions(output: ~str) -> TrieSet {
    let mut set = TrieSet::new();
    for line in output.line_iter(){
        if !session_is_attached(line) {
            match session_number(line) {
                Some(n) => { set.insert(n); }
                None => ()
            }
        }
    }
    set
}

#[fixed_stack_segment]
fn exec_program(program: &str, args: &[~str]) {
    do program.with_c_str() |c_program| {
        // I don't much care about the ownership of the strings here
        // at this point, so let's just fail if execvp isn't working.
        unsafe {
            let mut c_args = args.map(|arg| { arg.to_c_str().unwrap() });
            c_args.push(ptr::null());
            execvp(c_program, vec::raw::to_ptr(c_args));
            perror("Running tmux failed:".to_c_str().unwrap());
        }
        fail!(fmt!("Couldn't exec %s", program));
    }
}

fn main() {
    let proc = run::process_output("tmux", &[~"list-sessions"]);
    if proc.status != 0 {
        fail!(fmt!("Tmux exited with an error status: %d", proc.status));
    }
    let output = str::from_utf8(proc.output);
    let sessions = detached_sessions(output);
    match sessions.iter().next() {
        Some(n) => {
            let session = n.to_str();
            exec_program("tmux", &[~"tmux", ~"attach-session", ~"-t", session]);
        }
        _ => { exec_program("tmux", &[~"tmux"]); }
    }
}

#[test]
fn test_session_is_attached() {
    assert!(session_is_attached("11: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65] (attached)"));
    assert!(!session_is_attached("20: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65]"));
}

#[test]
fn test_session_number_with_numbers(){
    match(session_number("11: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65] (attached)")) {
        Some(11) => (),
        Some(n) => fail!(fmt!("Should have returned 11, got %u!", n)),
        None => fail!("Should have returned something, got nothing"),
    }
}

#[test]
fn test_session_number_with_strings(){
    match(session_number("oink: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65] (attached)")) {
        Some(n) => fail!(fmt!("Should have returned None, got %u!", n)),
        None => ()
    }
}

#[test]
fn test_detached_sessions() {
    let set = detached_sessions(~"3: foo (attached)\n2: bar\n4: abz (attached)\nfoo: baz");
    assert!(set.contains(&2));

    // attached sessions:
    assert!(!set.contains(&3));
    assert!(!set.contains(&4));

    // missing session:
    assert!(!set.contains(&1));
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]
#![warn(rust_2018_idioms)]
#![doc(html_root_url = "https://docs.rs/amq-protocol-tcp/2.0.0-rc11/")]

//! # AMQP URI TCP connection handling
//!
//! amq-protocol-tcp is a library aiming at providing tools to help
//! connecting to an AMQP URI

use amq_protocol_uri::{AMQPScheme, AMQPUri};
use tcp_stream::{HandshakeError, TcpStream};

use std::{
    io,
    net::SocketAddr,
};

/// Trait providing a method to connect to a TcpStream
pub trait AMQPUriTcpExt {
    /// connect to a TcpStream
    fn connect<S, F: FnOnce(TcpStream, AMQPUri) -> S>(self, f: F) -> io::Result<S>;
}

impl AMQPUriTcpExt for AMQPUri {
    fn connect<S, F: FnOnce(TcpStream, AMQPUri) -> S>(self, f: F) -> io::Result<S> {
        if let Ok(ipaddr) = format!("{}:{}", self.authority.host, self.authority.port).parse() {
            let stream = TcpStream::connect(&SocketAddr::new(ipaddr, self.authority.port))?;
            match self.scheme {
                AMQPScheme::AMQP  => Ok(stream),
                AMQPScheme::AMQPS => stream.into_tls(&self.authority.host).or_else(retry_handshake),
            }.map(|s| f(s, self))
        } else {
            Err(io::Error::new(io::ErrorKind::AddrNotAvailable, format!("cannot resolve {}", &self.authority.host)))
        }
    }
}

fn retry_handshake(error: HandshakeError) -> io::Result<TcpStream> {
    match error {
        HandshakeError::Failure(io_err) => Err(io_err),
        HandshakeError::WouldBlock(mid) => mid.handshake().or_else(retry_handshake),
    }
}

impl AMQPUriTcpExt for &str {
    fn connect<S, F: FnOnce(TcpStream, AMQPUri) -> S>(self, f: F) -> io::Result<S> {
        self.parse::<AMQPUri>().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?.connect(f)
    }
}
<commit_msg>tcp: reexport TcpStream<commit_after>#![deny(missing_docs)]
#![warn(rust_2018_idioms)]
#![doc(html_root_url = "https://docs.rs/amq-protocol-tcp/2.0.0-rc11/")]

//! # AMQP URI TCP connection handling
//!
//! amq-protocol-tcp is a library aiming at providing tools to help
//! connecting to an AMQP URI

use amq_protocol_uri::{AMQPScheme, AMQPUri};
use tcp_stream::HandshakeError;

use std::{
    io,
    net::SocketAddr,
};

/// Re-export TcpStream
pub use tcp_stream::TcpStream;

/// Trait providing a method to connect to a TcpStream
pub trait AMQPUriTcpExt {
    /// connect to a TcpStream
    fn connect<S, F: FnOnce(TcpStream, AMQPUri) -> S>(self, f: F) -> io::Result<S>;
}

impl AMQPUriTcpExt for AMQPUri {
    fn connect<S, F: FnOnce(TcpStream, AMQPUri) -> S>(self, f: F) -> io::Result<S> {
        if let Ok(ipaddr) = format!("{}:{}", self.authority.host, self.authority.port).parse() {
            let stream = TcpStream::connect(&SocketAddr::new(ipaddr, self.authority.port))?;
            match self.scheme {
                AMQPScheme::AMQP  => Ok(stream),
                AMQPScheme::AMQPS => stream.into_tls(&self.authority.host).or_else(retry_handshake),
            }.map(|s| f(s, self))
        } else {
            Err(io::Error::new(io::ErrorKind::AddrNotAvailable, format!("cannot resolve {}", &self.authority.host)))
        }
    }
}

fn retry_handshake(error: HandshakeError) -> io::Result<TcpStream> {
    match error {
        HandshakeError::Failure(io_err) => Err(io_err),
        HandshakeError::WouldBlock(mid) => mid.handshake().or_else(retry_handshake),
    }
}

impl AMQPUriTcpExt for &str {
    fn connect<S, F: FnOnce(TcpStream, AMQPUri) -> S>(self, f: F) -> io::Result<S> {
        self.parse::<AMQPUri>().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?.connect(f)
    }
}
<|endoftext|>


<commit_before>use proc_macro2::{Span, TokenStream};
use syn::{Ident, Type};

pub fn bundle_impl(
    trait_name: &str,
    type_name: Ident,
    internal: bool,
    block: TokenStream,
) -> TokenStream {
    let const_name = Ident::new(
        &format!("_palette_derive_{}_for_{}", trait_name, type_name),
        Span::call_site(),
    );

    if internal {
        quote!{
            #[allow(non_snake_case, unused_attributes, unused_qualifications)]
            mod #const_name {
                extern crate num_traits as _num_traits;
                use float::Float as _FloatTrait;
                use super::#type_name;
                #block
            }
        }
    } else {
        quote!{
            #[allow(non_snake_case, unused_attributes, unused_qualifications)]
            mod #const_name {
                extern crate palette as _palette;
                extern crate num_traits as _num_traits;
                use self::_palette::float::Float as _FloatTrait;
                use super::#type_name;
                #block
            }
        }
    }
}

pub fn path(path: &[&str], internal: bool) -> TokenStream {
    let path = path
        .into_iter()
        .map(|&ident| Ident::new(ident, Span::call_site()));

    if internal {
        quote!{::#(#path)::*}
    } else {
        quote!{self::_palette::#(#path)::*}
    }
}

pub fn path_type(path: &[&str], internal: bool) -> Type {
    let path = path
        .into_iter()
        .map(|&ident| Ident::new(ident, Span::call_site()));

    if internal {
        parse_quote!{::#(#path)::*}
    } else {
        parse_quote!{self::_palette::#(#path)::*}
    }
}

pub fn color_path(color: &str, internal: bool) -> TokenStream {
    match color {
        "Luma" => path(&["luma", "Luma"], internal),
        "Rgb" => path(&["rgb", "Rgb"], internal),
        _ => path(&[color], internal),
    }
}
<commit_msg>[WIP no_std] Fixed warnings coming from deriving Pixel<commit_after>use proc_macro2::{Span, TokenStream};
use syn::{Ident, Type};

pub fn bundle_impl(
    trait_name: &str,
    type_name: Ident,
    internal: bool,
    block: TokenStream,
) -> TokenStream {
    let const_name = Ident::new(
        &format!("_palette_derive_{}_for_{}", trait_name, type_name),
        Span::call_site(),
    );

    if internal {
        quote!{
            #[allow(non_snake_case, unused_attributes, unused_qualifications, unused_imports)]
            mod #const_name {
                use float::Float as _FloatTrait;
                use super::#type_name;
                #block
            }
        }
    } else {
        quote!{
            #[allow(non_snake_case, unused_attributes, unused_qualifications, unused_imports)]
            mod #const_name {
                extern crate palette as _palette;
                use self::_palette::float::Float as _FloatTrait;
                use super::#type_name;
                #block
            }
        }
    }
}

pub fn path(path: &[&str], internal: bool) -> TokenStream {
    let path = path
        .into_iter()
        .map(|&ident| Ident::new(ident, Span::call_site()));

    if internal {
        quote!{::#(#path)::*}
    } else {
        quote!{self::_palette::#(#path)::*}
    }
}

pub fn path_type(path: &[&str], internal: bool) -> Type {
    let path = path
        .into_iter()
        .map(|&ident| Ident::new(ident, Span::call_site()));

    if internal {
        parse_quote!{::#(#path)::*}
    } else {
        parse_quote!{self::_palette::#(#path)::*}
    }
}

pub fn color_path(color: &str, internal: bool) -> TokenStream {
    match color {
        "Luma" => path(&["luma", "Luma"], internal),
        "Rgb" => path(&["rgb", "Rgb"], internal),
        _ => path(&[color], internal),
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::panic;

use rustc_macro::{TokenStream, __internal};
use syntax::ast::{self, ItemKind};
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::fold::{self, Folder};
use errors::FatalError;

pub struct CustomDerive {
    inner: fn(TokenStream) -> TokenStream,
}

impl CustomDerive {
    pub fn new(inner: fn(TokenStream) -> TokenStream) -> CustomDerive {
        CustomDerive { inner: inner }
    }
}

impl MultiItemModifier for CustomDerive {
    fn expand(&self,
              ecx: &mut ExtCtxt,
              span: Span,
              _meta_item: &ast::MetaItem,
              item: Annotatable)
              -> Vec<Annotatable> {
        let item = match item {
            Annotatable::Item(item) => item,
            Annotatable::ImplItem(_) |
            Annotatable::TraitItem(_) => {
                ecx.span_err(span, "custom derive attributes may only be \
                                    applied to struct/enum items");
                return Vec::new()
            }
        };
        match item.node {
            ItemKind::Struct(..) |
            ItemKind::Enum(..) => {}
            _ => {
                ecx.span_err(span, "custom derive attributes may only be \
                                    applied to struct/enum items");
                return Vec::new()
            }
        }

        let input = __internal::new_token_stream(item);
        let res = __internal::set_parse_sess(&ecx.parse_sess, || {
            let inner = self.inner;
            panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input)))
        });
        let item = match res {
            Ok(stream) => __internal::token_stream_items(stream),
            Err(e) => {
                let msg = "custom derive attribute panicked";
                let mut err = ecx.struct_span_fatal(span, msg);
                if let Some(s) = e.downcast_ref::<String>() {
                    err.help(&format!("message: {}", s));
                }
                if let Some(s) = e.downcast_ref::<&'static str>() {
                    err.help(&format!("message: {}", s));
                }

                err.emit();
                panic!(FatalError);
            }
        };

        // Right now we have no knowledge of spans at all in custom derive
        // macros, everything is just parsed as a string. Reassign all spans to
        // the #[derive] attribute for better errors here.
        item.into_iter().flat_map(|item| {
            ChangeSpan { span: span }.fold_item(item)
        }).map(Annotatable::Item).collect()
    }
}

struct ChangeSpan { span: Span }

impl Folder for ChangeSpan {
    fn new_span(&mut self, _sp: Span) -> Span {
        self.span
    }

    fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
        fold::noop_fold_mac(mac, self)
    }
}
<commit_msg>Point macros 1.1 errors to the input item<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::panic;

use rustc_macro::{TokenStream, __internal};
use syntax::ast::{self, ItemKind};
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::fold::{self, Folder};
use errors::FatalError;

pub struct CustomDerive {
    inner: fn(TokenStream) -> TokenStream,
}

impl CustomDerive {
    pub fn new(inner: fn(TokenStream) -> TokenStream) -> CustomDerive {
        CustomDerive { inner: inner }
    }
}

impl MultiItemModifier for CustomDerive {
    fn expand(&self,
              ecx: &mut ExtCtxt,
              span: Span,
              _meta_item: &ast::MetaItem,
              item: Annotatable)
              -> Vec<Annotatable> {
        let item = match item {
            Annotatable::Item(item) => item,
            Annotatable::ImplItem(_) |
            Annotatable::TraitItem(_) => {
                ecx.span_err(span, "custom derive attributes may only be \
                                    applied to struct/enum items");
                return Vec::new()
            }
        };
        match item.node {
            ItemKind::Struct(..) |
            ItemKind::Enum(..) => {}
            _ => {
                ecx.span_err(span, "custom derive attributes may only be \
                                    applied to struct/enum items");
                return Vec::new()
            }
        }

        let input_span = item.span;
        let input = __internal::new_token_stream(item);
        let res = __internal::set_parse_sess(&ecx.parse_sess, || {
            let inner = self.inner;
            panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input)))
        });
        let item = match res {
            Ok(stream) => __internal::token_stream_items(stream),
            Err(e) => {
                let msg = "custom derive attribute panicked";
                let mut err = ecx.struct_span_fatal(span, msg);
                if let Some(s) = e.downcast_ref::<String>() {
                    err.help(&format!("message: {}", s));
                }
                if let Some(s) = e.downcast_ref::<&'static str>() {
                    err.help(&format!("message: {}", s));
                }

                err.emit();
                panic!(FatalError);
            }
        };

        // Right now we have no knowledge of spans at all in custom derive
        // macros, everything is just parsed as a string. Reassign all spans to
        // the input `item` for better errors here.
        item.into_iter().flat_map(|item| {
            ChangeSpan { span: input_span }.fold_item(item)
        }).map(Annotatable::Item).collect()
    }
}

struct ChangeSpan { span: Span }

impl Folder for ChangeSpan {
    fn new_span(&mut self, _sp: Span) -> Span {
        self.span
    }

    fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
        fold::noop_fold_mac(mac, self)
    }
}
<|endoftext|>


<commit_before>// https://leetcode.com/problems/minimum-number-of-buckets-required-to-collect-rainwater-from-houses/
pub fn minimum_buckets(street: String) -> i32 {
    todo!()
}

fn main() {
    println!("{}", minimum_buckets(String::from("H..H"))); // 2
    println!("{}", minimum_buckets(String::from(".H.H."))); // 1
    println!("{}", minimum_buckets(String::from(".HHH."))); // -1
    println!("{}", minimum_buckets(String::from(".H..H..H."))); // 3
    println!("{}", minimum_buckets(String::from(".H.H..H."))); // 2
    println!("{}", minimum_buckets(String::from(".H.H.H."))); // 2
    println!("{}", minimum_buckets(String::from("HH..HH"))); // -1
}
<commit_msg>Solve problem 2086<commit_after>use std::collections::HashSet;

// https://leetcode.com/problems/minimum-number-of-buckets-required-to-collect-rainwater-from-houses/
pub fn minimum_buckets(street: String) -> i32 {
    let mut i = 0;
    let chars: Vec<char> = street.chars().collect();
    let mut buckets: HashSet<usize> = HashSet::new();
    let mut house_count = 0;
    while i < chars.len() {
        if chars[i] == 'H' {
            house_count += 1;
        }
        if (i == 0 && i + 1 < chars.len() && chars[i] == 'H' && chars[i + 1] == 'H')
            || (i > 0 && i == chars.len() - 1 && chars[i] == 'H' && chars[i - 1] == 'H')
            || i > 0
                && i + 1 < chars.len()
                && chars[i - 1] == 'H'
                && chars[i] == 'H'
                && chars[i + 1] == 'H'
        {
            return -1;
        }
        if i != 0 && buckets.contains(&(i - 1)) {
            i += 1;
            continue;
        }
        if i + 1 < chars.len() && chars[i] == 'H' && chars[i + 1] == '.' {
            buckets.insert(i + 1);
        } else if i != 0 && chars[i] == 'H' && chars[i - 1] == '.' {
            buckets.insert(i - 1);
        }
        i += 1;
    }
    if house_count == 0 {
        0
    } else if buckets.len() == 0 {
        -1
    } else {
        buckets.len() as i32
    }
}

fn main() {
    println!("{}", minimum_buckets(String::from("H..H"))); // 2
    println!("{}", minimum_buckets(String::from(".H.H."))); // 1
    println!("{}", minimum_buckets(String::from(".HHH."))); // -1
    println!("{}", minimum_buckets(String::from(".H..H..H."))); // 3
    println!("{}", minimum_buckets(String::from(".H.H..H."))); // 2
    println!("{}", minimum_buckets(String::from(".H.H.H."))); // 2
    println!("{}", minimum_buckets(String::from("HH..HH"))); // -1
    println!("{}", minimum_buckets(String::from("H"))); // -1
    println!("{}", minimum_buckets(String::from("...."))); // 0
    println!("{}", minimum_buckets(String::from(".HH.H.H.H.."))); // 3
}
<|endoftext|>


<commit_before>use addressable::Addressable;
use addressable::AddressableUtil;

static NMI_VECTOR: u16 = 0xfffa;
static RESET_VECTOR: u16 = 0xfffc;
static IRQ_VECTOR: u16 = 0xfffe;

struct Registers {
	pc: u16,				// program counter
	ac: u8,					// accumulator
	x: u8,					// x register
	y: u8,					// y register
	sr: u8,					// status register (NV-BDIZC: Negative, oVerflow, Break, Decimal, Interrupt, Zero, Carry)
	sp: u8,					// stack pointer
}

enum Operand {
	Implied,							// OPC				// operand implied
	Immediate(u8),						// OPC #$BB			// operand is byte (BB)
	Accumulator,						// OPC A			// operand is AC
	Relative(i8),						// OPC $BB			// branch target is PC + offset (BB), bit 7 signifies negative offset
	Absolute(u16),						// OPC $HHLL		// operand is address $HHLL
	AbsoluteIndexedWithX(u16),			// OPC $HHLL,X		// operand is address $HHLL incremented by X with carry
	AbsoluteIndexedWithY(u16),			// OPC $HHLL,Y		// operand is address $HHLL incremented by Y with carry
	Indirect(u16),						// OPC ($HHLL)		// operand is effective address; effective address is value of address
	ZeroPage(u8),						// OPC $LL			// operand is address $00LL
	ZeroPageIndexedWithX(u8),			// OPC $LL,X		// operand is address $00LL incremented by X, no page transition
	ZeroPageIndexedWithY(u8),			// OPC $LL,Y		// operand is address $00LL incremented by Y, no page transition
	ZeroPageIndexedWithXIndirect(u8),	// OPC ($LL,X)		// operand is effective address; effective address is $00LL incremented by X without carry
	ZeroPageIndirectIndexedWithY(u8),	// OPC ($LL),Y		// operand is effective address incremented by Y with carry; effective address is word at $00LL
}

impl Operand {
	fn addr<M: Addressable<u16>> (&self, cpu: &Mos6502<M>) -> u16 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand is never targetted to an address"),
			Immediate(_)						=> fail!("mos6510: Immediade operand is never targetted to an address"),
			Accumulator							=> fail!("mos6510: Accumulator operand is never targetted to an address"),
			Relative(offset)					=> cpu.reg.pc + offset as u16,
			Absolute(addr)						=> addr,
			AbsoluteIndexedWithX(addr)			=> addr + cpu.reg.x as u16,
			AbsoluteIndexedWithY(addr)			=> addr + cpu.reg.y as u16,
			Indirect(addr)						=> cpu.mem.get_le(addr),
			ZeroPage(addr)						=> addr as u16,
			ZeroPageIndexedWithX(addr)			=> (addr + cpu.reg.x) as u16,					// no page transition
			ZeroPageIndexedWithY(addr)			=> (addr + cpu.reg.y) as u16,					// no page transition
			ZeroPageIndexedWithXIndirect(addr)	=> cpu.mem.get_le((addr + cpu.reg.x) as u16),	// no page transition
			ZeroPageIndirectIndexedWithY(addr)	=> { let iaddr: u16 = cpu.mem.get_le(addr as u16); iaddr + cpu.reg.y as u16 },
		}
	}

	fn get<M: Addressable<u16>> (&self, cpu: &Mos6502<M>) -> u8 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never has a value"),
			Immediate(val)						=> val,
			Accumulator							=> cpu.reg.ac,
			Relative(_target)					=> fail!("mos6510: Relative operand never has a value"),
			op									=> { let addr = op.addr(cpu); cpu.mem.get(addr) },
		}
	}

	fn set<M: Addressable<u16>> (&self, cpu: &mut Mos6502<M>, val: u8) {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never sets a value"),
			Immediate(_)						=> fail!("mos6510: Immediate operand never sets a value"),
			Accumulator							=> cpu.reg.ac = val,
			Relative(_target)					=> fail!("mos6510: Relative operand never sets a value"),
			op									=> { let addr = op.addr(cpu); cpu.mem.set(addr, val); },
		}
	}
}

pub struct Mos6502<M> {
	priv reg: Registers,					// internal CPU registers
	priv mem: M,							// memory as accessible to the CPU
}

impl<M: Addressable<u16>> Mos6502<M> {
	pub fn new (mem: M) -> Mos6502<M> {
		Mos6502 {
			reg: Registers { pc: 0, ac: 0, x: 0, y: 0, sr: 0, sp: 0 },
			mem: mem,
		}
	}
}

pub struct Mos6510<'self> {
	priv cpu: Mos6502<'self>,				// Core CPU
	priv port_ddr: u8,						// CPU port data direction register
	priv port_dat: u8,						// CPU port data register
}

impl<'self> Mos6510<'self> {
	pub fn new (mem: &'self Memory<u16>) -> Mos6510<'self> {
		// TODO: addresses $0000 (data direction) and $0001 (data) are hardwired for the processor I/O port
		Mos6510 { cpu: Mos6502::new(mem), port_ddr: 0, port_dat: 0 }
	}
}
<commit_msg>Added methods to get next opcode and argument<commit_after>use addressable::Addressable;
use addressable::AddressableUtil;

static NMI_VECTOR: u16 = 0xfffa;
static RESET_VECTOR: u16 = 0xfffc;
static IRQ_VECTOR: u16 = 0xfffe;

struct Registers {
	pc: u16,				// program counter
	ac: u8,					// accumulator
	x: u8,					// x register
	y: u8,					// y register
	sr: u8,					// status register (NV-BDIZC: Negative, oVerflow, Break, Decimal, Interrupt, Zero, Carry)
	sp: u8,					// stack pointer
}

enum Operand {
	Implied,							// OPC				// operand implied
	Immediate(u8),						// OPC #$BB			// operand is byte (BB)
	Accumulator,						// OPC A			// operand is AC
	Relative(i8),						// OPC $BB			// branch target is PC + offset (BB), bit 7 signifies negative offset
	Absolute(u16),						// OPC $HHLL		// operand is address $HHLL
	AbsoluteIndexedWithX(u16),			// OPC $HHLL,X		// operand is address $HHLL incremented by X with carry
	AbsoluteIndexedWithY(u16),			// OPC $HHLL,Y		// operand is address $HHLL incremented by Y with carry
	Indirect(u16),						// OPC ($HHLL)		// operand is effective address; effective address is value of address
	ZeroPage(u8),						// OPC $LL			// operand is address $00LL
	ZeroPageIndexedWithX(u8),			// OPC $LL,X		// operand is address $00LL incremented by X, no page transition
	ZeroPageIndexedWithY(u8),			// OPC $LL,Y		// operand is address $00LL incremented by Y, no page transition
	ZeroPageIndexedWithXIndirect(u8),	// OPC ($LL,X)		// operand is effective address; effective address is $00LL incremented by X without carry
	ZeroPageIndirectIndexedWithY(u8),	// OPC ($LL),Y		// operand is effective address incremented by Y with carry; effective address is word at $00LL
}

impl Operand {
	fn addr<M: Addressable<u16>> (&self, cpu: &Mos6502<M>) -> u16 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand is never targetted to an address"),
			Immediate(_)						=> fail!("mos6510: Immediade operand is never targetted to an address"),
			Accumulator							=> fail!("mos6510: Accumulator operand is never targetted to an address"),
			Relative(offset)					=> cpu.reg.pc + offset as u16,
			Absolute(addr)						=> addr,
			AbsoluteIndexedWithX(addr)			=> addr + cpu.reg.x as u16,
			AbsoluteIndexedWithY(addr)			=> addr + cpu.reg.y as u16,
			Indirect(addr)						=> cpu.mem.get_le(addr),
			ZeroPage(addr)						=> addr as u16,
			ZeroPageIndexedWithX(addr)			=> (addr + cpu.reg.x) as u16,					// no page transition
			ZeroPageIndexedWithY(addr)			=> (addr + cpu.reg.y) as u16,					// no page transition
			ZeroPageIndexedWithXIndirect(addr)	=> cpu.mem.get_le((addr + cpu.reg.x) as u16),	// no page transition
			ZeroPageIndirectIndexedWithY(addr)	=> { let iaddr: u16 = cpu.mem.get_le(addr as u16); iaddr + cpu.reg.y as u16 },
		}
	}

	fn get<M: Addressable<u16>> (&self, cpu: &Mos6502<M>) -> u8 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never has a value"),
			Immediate(val)						=> val,
			Accumulator							=> cpu.reg.ac,
			Relative(_target)					=> fail!("mos6510: Relative operand never has a value"),
			op									=> { let addr = op.addr(cpu); cpu.mem.get(addr) },
		}
	}

	fn set<M: Addressable<u16>> (&self, cpu: &mut Mos6502<M>, val: u8) {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never sets a value"),
			Immediate(_)						=> fail!("mos6510: Immediate operand never sets a value"),
			Accumulator							=> cpu.reg.ac = val,
			Relative(_target)					=> fail!("mos6510: Relative operand never sets a value"),
			op									=> { let addr = op.addr(cpu); cpu.mem.set(addr, val); },
		}
	}
}

pub struct Mos6502<M> {
	priv reg: Registers,					// internal CPU registers
	priv mem: M,							// memory as accessible to the CPU
}

impl<M: Addressable<u16>> Mos6502<M> {
	pub fn new (mem: M) -> Mos6502<M> {
		Mos6502 {
			reg: Registers { pc: 0, ac: 0, x: 0, y: 0, sr: 0, sp: 0 },
			mem: mem,
		}
	}

	fn get_opcode (&self) -> u8 {
		self.mem.get(self.reg.pc)
	}

	fn get_argument<T: Int> (&self) -> T {
		self.mem.get_le(self.reg.pc + 1)
	}
}

pub struct Mos6510<'self> {
	priv cpu: Mos6502<'self>,				// Core CPU
	priv port_ddr: u8,						// CPU port data direction register
	priv port_dat: u8,						// CPU port data register
}

impl<'self> Mos6510<'self> {
	pub fn new (mem: &'self Memory<u16>) -> Mos6510<'self> {
		// TODO: addresses $0000 (data direction) and $0001 (data) are hardwired for the processor I/O port
		Mos6510 { cpu: Mos6502::new(mem), port_ddr: 0, port_dat: 0 }
	}
}
<|endoftext|>