<commit_before>extern crate pancurses;

use pancurses::*;

fn main() {
    let window = initscr();

    start_color();
    use_default_colors();
    set_blink(true);

    cbreak();
    noecho();

    window.clear();
    window.refresh();

    window.keypad(true);

    init_pair(1, 15, COLOR_BLACK);
    init_pair(2, COLOR_BLACK, COLOR_YELLOW);

    set_title("NewTest: tests various pancurses features");

    mousemask(ALL_MOUSE_EVENTS, std::ptr::null_mut());

    window.attrset(COLOR_PAIR(1));

    let mut quit = false;
    let mut redraw = true;

    const COL1: i32 = 2;
    const COL2: i32 = COL1 + 30;
    const COL3: i32 = 72;
    const N_CURSORS: i32 = 9;

    let mut unicode_offset = 0x80;
    let mut blink_state = true;

    let mut cursor_state_1 = 2;
    let mut cursor_state_2 = 3;

    while !quit {
        let (y_max, x_max) = window.get_max_yx();
        let color_block_start = 54;
        let mut color_block_cols = (x_max - color_block_start) / 2;
        let color_block_lines = 19;

        let cursor_state_text = ["Invisible (click to change) ",
                                 "Underscore (click to change)",
                                 "Block (click to change)     ",
                                 "Outline (click to change)   ",
                                 "Caret (click to change)     ",
                                 "Half-block (click to change)",
                                 "Central (click to change)   ",
                                 "Cross (click to change)     ",
                                 "Heavy box (click to change) "];

        if color_block_cols < 0 {
            color_block_cols = 0;
        }

        if redraw {
            window.mvaddstr(1, COL1, "'Normal' white-on-black");

            window.attron(A_DIM);
            window.mvaddstr(2, COL1, "Dimmed text");
            window.attroff(A_DIM);

            window.attron(A_BLINK);
            window.mvaddstr(6, 40, "Blinking");
            window.attron(A_BOLD);
            window.mvaddstr(8, 40, "BlinkBold");
            window.attron(A_ITALIC);
            window.mvaddstr(0, COL2, "BlinkBoldItalic");
            window.attrset(COLOR_PAIR(3));
            window.attron(A_UNDERLINE);

            window.mvaddstr(1, COL2, "Underlined");

            window.attrset(COLOR_PAIR(1));
            window.attron(A_UNDERLINE | A_ITALIC);
            window.mvaddstr(2, COL2, "UnderlinedItalic");
            window.attrset(COLOR_PAIR(2));
            window.attron(A_BLINK);
            window.mvaddstr(4, COL1, "Black-on-yellow blinking");

            window.attrset(COLOR_PAIR(1));
            window.mv(4, COL2);
            text_in_a_box("Text in a box", &window);

            window.attrset(COLOR_PAIR(6));
            window.attron(A_STRIKEOUT);
            window.mvaddstr(10, 40, "Strikeout");
            window.attrset(COLOR_PAIR(1));

            window.mv(11, 40);
            text_in_a_box("Next Ucode pg", &window);
            if unicode_offset != 0 {
                window.mv(12, 40);
                text_in_a_box("Prev Ucode pg", &window);
            }
            window.mvprintw(13, 40, &format!("U+{:04X} ", unicode_offset));

            for i in 0..128 {
                // Show extended characters
                window.mvaddstr(5 + i % 16,
                                (i / 16) * 5,
                                &format!("{:02X} ", i + unicode_offset));
                if i + unicode_offset > ' ' as i32 {
                    window.addch((i + unicode_offset) as chtype);
                } else {
                    window.addch(' ');
                }
                window.addch(' ');
            }

            // Skipped the full RGB example, I don't think there's a suitable feature in ncurses

            redraw = false;
            window.attrset(COLOR_PAIR(1));

            for i in 0..6 {
                let line = 24 + i / 3;
                let col = 5 + 25 * (i % 3);

                let spanish = "Español";
                let russian = "Русский язык";
                let greek = "Ελληνικά";
                let georgian = "ქართული ენა";
                let fullwidth = " Ｆｕｌｌｗｉｄｔｈ";
                let combining_marks = "Cmin 	r";

                let texts = [spanish, russian, greek, georgian, fullwidth, combining_marks];

                if line < y_max && col < x_max {
                    window.mvaddnstr(line, 5 + 25 * (i % 3), texts[i as usize], x_max - col);
                }
            }

            window.mvaddstr(1, COL3, "Click on cursor descriptions to");
            window.mvaddstr(2, COL3, "cycle through possible cursors");
            window.mvaddstr(3, COL3, "Click on colors at left to change");
            window.mvaddstr(4, COL3, "colors used for under/over/outlining");
            window.mvaddstr(5, COL3, "Click 'Blink' at bottom to toggle");
            window.mvaddstr(6, COL3, "'real' blinking vs. 'highlit' blink");
        }

        window.mvaddnstr(19,
                         color_block_start,
                         cursor_state_text[cursor_state_1],
                         x_max - color_block_start);
        window.mvaddnstr(20,
                         color_block_start,
                         cursor_state_text[cursor_state_2],
                         x_max - color_block_start);

        for i in 0..color_block_cols * color_block_lines {
            let n_color_blocks = 256;

            window.attrset(COLOR_PAIR(if i >= n_color_blocks {
                2
            } else {
                i as chtype
            }));
            if i > 2 && i < n_color_blocks {
                init_pair(i as i16, i as i16, COLOR_BLACK);
            }
            if i % color_block_cols == 0 {
                window.mv(i / color_block_cols, color_block_start);
            }
            window.attron(A_REVERSE);
            window.addstr(" ");
        }

        window.mv(19, color_block_start - 3);
        window.refresh();
        let c = window.getch();
        window.attrset(COLOR_PAIR(1));

        match c {
            Some(Input::KeyF1) => quit = true,
            Some(Input::Character(x)) if x == 27 as char => quit = true,
            Some(Input::KeyF2) => {
                blink_state = !blink_state;
                set_blink(blink_state);
            }
            Some(x) if x != Input::KeyMouse => {
                window.mvaddstr(0, COL1, &format!("Key {:?} hit          ", x));
            }
            Some(Input::KeyMouse) => {
                match getmouse() {
                    Ok(mouse_event) => {
                        window.mvaddstr(0,
                                        COL1,
                                        &format!("Mouse at {} x {}: {}",
                                                 mouse_event.x,
                                                 mouse_event.y,
                                                 mouse_event.bstate));
                        if mouse_event.x >= color_block_start {
                            if mouse_event.y == 19 { // blink/non-blink toggle
                                cursor_state_1 = (cursor_state_1 + 1) % N_CURSORS as usize;
                            } else if mouse_event.y == 20 { // cycle cursor state
                                cursor_state_2 = (cursor_state_2 + 1) % N_CURSORS as usize;
                            }
                        } else if mouse_event.x >= 40 && mouse_event.x < 40 + 10 {
                            if mouse_event.y == 11 {
                                redraw = true;
                                unicode_offset += 0x80;
                            } else if (mouse_event.y == 12 && unicode_offset != 0) {
                                redraw = true;
                                unicode_offset -= 0x80;
                            }
                        }
                    }
                    Err(_) => (),
                }
            }
            _ => (),
        }
    }

    endwin();
}

fn text_in_a_box(text: &str, window: &Window) {
    let len = text.len();

    window.attron(A_OVERLINE | A_UNDERLINE | A_LEFTLINE);
    if len == 1 {
        window.attron(A_RIGHTLINE);
    }

    window.addnstr(text, 1);
    if len > 1 {
        window.attroff(A_LEFTLINE);
        if len > 2 {
            window.addnstr(&text[1..], len - 2);
        }
        window.attron(A_RIGHTLINE);
        window.addnstr(&text[len - 1..], 1);
    }

    window.attroff(A_OVERLINE | A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE);
}
<commit_msg>Removed unnecessary parentheses<commit_after>extern crate pancurses;

use pancurses::*;

fn main() {
    let window = initscr();

    start_color();
    use_default_colors();
    set_blink(true);

    cbreak();
    noecho();

    window.clear();
    window.refresh();

    window.keypad(true);

    init_pair(1, 15, COLOR_BLACK);
    init_pair(2, COLOR_BLACK, COLOR_YELLOW);

    set_title("NewTest: tests various pancurses features");

    mousemask(ALL_MOUSE_EVENTS, std::ptr::null_mut());

    window.attrset(COLOR_PAIR(1));

    let mut quit = false;
    let mut redraw = true;

    const COL1: i32 = 2;
    const COL2: i32 = COL1 + 30;
    const COL3: i32 = 72;
    const N_CURSORS: i32 = 9;

    let mut unicode_offset = 0x80;
    let mut blink_state = true;

    let mut cursor_state_1 = 2;
    let mut cursor_state_2 = 3;

    while !quit {
        let (y_max, x_max) = window.get_max_yx();
        let color_block_start = 54;
        let mut color_block_cols = (x_max - color_block_start) / 2;
        let color_block_lines = 19;

        let cursor_state_text = ["Invisible (click to change) ",
                                 "Underscore (click to change)",
                                 "Block (click to change)     ",
                                 "Outline (click to change)   ",
                                 "Caret (click to change)     ",
                                 "Half-block (click to change)",
                                 "Central (click to change)   ",
                                 "Cross (click to change)     ",
                                 "Heavy box (click to change) "];

        if color_block_cols < 0 {
            color_block_cols = 0;
        }

        if redraw {
            window.mvaddstr(1, COL1, "'Normal' white-on-black");

            window.attron(A_DIM);
            window.mvaddstr(2, COL1, "Dimmed text");
            window.attroff(A_DIM);

            window.attron(A_BLINK);
            window.mvaddstr(6, 40, "Blinking");
            window.attron(A_BOLD);
            window.mvaddstr(8, 40, "BlinkBold");
            window.attron(A_ITALIC);
            window.mvaddstr(0, COL2, "BlinkBoldItalic");
            window.attrset(COLOR_PAIR(3));
            window.attron(A_UNDERLINE);

            window.mvaddstr(1, COL2, "Underlined");

            window.attrset(COLOR_PAIR(1));
            window.attron(A_UNDERLINE | A_ITALIC);
            window.mvaddstr(2, COL2, "UnderlinedItalic");
            window.attrset(COLOR_PAIR(2));
            window.attron(A_BLINK);
            window.mvaddstr(4, COL1, "Black-on-yellow blinking");

            window.attrset(COLOR_PAIR(1));
            window.mv(4, COL2);
            text_in_a_box("Text in a box", &window);

            window.attrset(COLOR_PAIR(6));
            window.attron(A_STRIKEOUT);
            window.mvaddstr(10, 40, "Strikeout");
            window.attrset(COLOR_PAIR(1));

            window.mv(11, 40);
            text_in_a_box("Next Ucode pg", &window);
            if unicode_offset != 0 {
                window.mv(12, 40);
                text_in_a_box("Prev Ucode pg", &window);
            }
            window.mvprintw(13, 40, &format!("U+{:04X} ", unicode_offset));

            for i in 0..128 {
                // Show extended characters
                window.mvaddstr(5 + i % 16,
                                (i / 16) * 5,
                                &format!("{:02X} ", i + unicode_offset));
                if i + unicode_offset > ' ' as i32 {
                    window.addch((i + unicode_offset) as chtype);
                } else {
                    window.addch(' ');
                }
                window.addch(' ');
            }

            // Skipped the full RGB example, I don't think there's a suitable feature in ncurses

            redraw = false;
            window.attrset(COLOR_PAIR(1));

            for i in 0..6 {
                let line = 24 + i / 3;
                let col = 5 + 25 * (i % 3);

                let spanish = "Español";
                let russian = "Русский язык";
                let greek = "Ελληνικά";
                let georgian = "ქართული ენა";
                let fullwidth = " Ｆｕｌｌｗｉｄｔｈ";
                let combining_marks = "Cmin 	r";

                let texts = [spanish, russian, greek, georgian, fullwidth, combining_marks];

                if line < y_max && col < x_max {
                    window.mvaddnstr(line, 5 + 25 * (i % 3), texts[i as usize], x_max - col);
                }
            }

            window.mvaddstr(1, COL3, "Click on cursor descriptions to");
            window.mvaddstr(2, COL3, "cycle through possible cursors");
            window.mvaddstr(3, COL3, "Click on colors at left to change");
            window.mvaddstr(4, COL3, "colors used for under/over/outlining");
            window.mvaddstr(5, COL3, "Click 'Blink' at bottom to toggle");
            window.mvaddstr(6, COL3, "'real' blinking vs. 'highlit' blink");
        }

        window.mvaddnstr(19,
                         color_block_start,
                         cursor_state_text[cursor_state_1],
                         x_max - color_block_start);
        window.mvaddnstr(20,
                         color_block_start,
                         cursor_state_text[cursor_state_2],
                         x_max - color_block_start);

        for i in 0..color_block_cols * color_block_lines {
            let n_color_blocks = 256;

            window.attrset(COLOR_PAIR(if i >= n_color_blocks {
                2
            } else {
                i as chtype
            }));
            if i > 2 && i < n_color_blocks {
                init_pair(i as i16, i as i16, COLOR_BLACK);
            }
            if i % color_block_cols == 0 {
                window.mv(i / color_block_cols, color_block_start);
            }
            window.attron(A_REVERSE);
            window.addstr(" ");
        }

        window.mv(19, color_block_start - 3);
        window.refresh();
        let c = window.getch();
        window.attrset(COLOR_PAIR(1));

        match c {
            Some(Input::KeyF1) => quit = true,
            Some(Input::Character(x)) if x == 27 as char => quit = true,
            Some(Input::KeyF2) => {
                blink_state = !blink_state;
                set_blink(blink_state);
            }
            Some(x) if x != Input::KeyMouse => {
                window.mvaddstr(0, COL1, &format!("Key {:?} hit          ", x));
            }
            Some(Input::KeyMouse) => {
                match getmouse() {
                    Ok(mouse_event) => {
                        window.mvaddstr(0,
                                        COL1,
                                        &format!("Mouse at {} x {}: {}",
                                                 mouse_event.x,
                                                 mouse_event.y,
                                                 mouse_event.bstate));
                        if mouse_event.x >= color_block_start {
                            if mouse_event.y == 19 { // blink/non-blink toggle
                                cursor_state_1 = (cursor_state_1 + 1) % N_CURSORS as usize;
                            } else if mouse_event.y == 20 { // cycle cursor state
                                cursor_state_2 = (cursor_state_2 + 1) % N_CURSORS as usize;
                            }
                        } else if mouse_event.x >= 40 && mouse_event.x < 40 + 10 {
                            if mouse_event.y == 11 {
                                redraw = true;
                                unicode_offset += 0x80;
                            } else if mouse_event.y == 12 && unicode_offset != 0 {
                                redraw = true;
                                unicode_offset -= 0x80;
                            }
                        }
                    }
                    Err(_) => (),
                }
            }
            _ => (),
        }
    }

    endwin();
}

fn text_in_a_box(text: &str, window: &Window) {
    let len = text.len();

    window.attron(A_OVERLINE | A_UNDERLINE | A_LEFTLINE);
    if len == 1 {
        window.attron(A_RIGHTLINE);
    }

    window.addnstr(text, 1);
    if len > 1 {
        window.attroff(A_LEFTLINE);
        if len > 2 {
            window.addnstr(&text[1..], len - 2);
        }
        window.attron(A_RIGHTLINE);
        window.addnstr(&text[len - 1..], 1);
    }

    window.attroff(A_OVERLINE | A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE);
}
<|endoftext|>


<commit_before>/* Copyright 2016 Mark Haines
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#[derive(PartialEq, Debug)]
pub struct Node {
    children: u32,
    length_in_bytes: u32,
}

pub fn parse(input: &[u8], output: &mut Vec<Node>, stack: &mut Vec<u32>) -> Result<(),()> {
    if parse_(input, output, stack) {
        Ok(())
    } else {
        Err(())
    }
}

fn parse_(input: &[u8], output: &mut Vec<Node>, stack: &mut Vec<u32>) -> bool {
    let mut iter = input.iter().peekable();
    let mut parsing_object = false;
    if iter.len() == 2 {
        output.push(Node {children: 0, length_in_bytes: 2});
        return true;
    }
    'node_end: loop {
        match stack.pop() {
            Some(offset_and_state) => {
                let output_index = output.len() as u32;
                let offset = offset_and_state >> 1;
                let node = match output.get_mut(offset as usize) {
                    Some(node) => node,
                    None => return false
                };
                node.length_in_bytes -= iter.len() as u32;
                node.children = output_index - offset - 1;
                let prev_offset_and_state = match stack.last() {
                    Some(value) => value,
                    None => return true
                };
                parsing_object = (prev_offset_and_state & 1) == 1;
                let input_char = match iter.next() {
                    None => return false,
                    Some(value) => *value
                };
                if input_char != b',' {
                    continue 'node_end;
                }
            },
            None => {}
        };
        'value_start: loop {
            if parsing_object {
                if iter.next() == None {
                    return false;
                }
                let start = iter.len() + 1;
                loop {
                    let input_char = match iter.next() {
                        None => return false,
                        Some(value) => *value
                    };
                    if input_char == b'"' {
                        break;
                    }
                    if input_char == b'\\' {
                        if iter.next() == None {
                            return false;
                        }
                    }
                }
                output.push(Node {
                    children: 0, length_in_bytes: (start - iter.len()) as u32
                });
                if iter.next() == None {
                    return false;
                }
            }
            let start = iter.len();
            let input_char = match iter.next() {
                None => return false,
                Some(value) => *value
            };
            if input_char == b'{' {
                let peek_char = match iter.peek() {
                    None => return false,
                    Some(value) => **value
                };
                if peek_char == b'}' {
                    output.push(Node {children: 0, length_in_bytes: 2});
                    let _ = iter.next();
                } else {
                    let index = output.len() as u32;
                    stack.push((index << 1) | 1);
                    output.push(Node {
                        children: 0, length_in_bytes: start as u32,
                    });
                    parsing_object = true;
                    continue 'value_start;
                }
            } else if input_char == b'[' {
                let peek_char = match iter.peek() {
                    None => return false,
                    Some(value) => **value
                };
                if peek_char == b']' {
                    output.push(Node {children: 0, length_in_bytes: 2});
                    let _ = iter.next();
                } else {
                    let index = output.len() as u32;
                    stack.push(index << 1);
                    output.push(Node {
                        children: 0, length_in_bytes: start as u32,
                    });
                    parsing_object = false;
                    continue 'value_start;
                }
            } else if input_char == b'"' {
                loop {
                    let input_char = match iter.next() {
                        None => return false,
                        Some(value) => *value
                    };
                    if input_char == b'"' {
                        break;
                    }
                    if input_char == b'\\' {
                        if iter.next() == None {
                            return false;
                        }
                    }
                }
                output.push(Node {
                    children: 0,
                    length_in_bytes: (start - iter.len()) as u32
                });
            } else {
                loop {
                    let input_char = match iter.next() {
                        None => return false,
                        Some(value) => *value
                    };
                    if input_char == b',' {
                        output.push(Node {
                            children: 0,
                            length_in_bytes: (start - iter.len() - 1) as u32
                        });
                        continue 'value_start;
                    } else if (input_char & 0xDF) == b']' {
                        output.push(Node {
                            children: 0,
                            length_in_bytes: (start - iter.len() - 1) as u32
                        });
                        continue 'node_end;
                    }
                }
            }
            let input_char = match iter.next() {
                None => return false,
                Some(value) => *value
            };
            if input_char == b',' {
                continue 'value_start;
            } else {
                continue 'node_end;
            }
        }
    }
}

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

    fn parse(input: &[u8]) -> Vec<Node> {
        let mut output : Vec<Node> = Vec::new();
        let mut stack : Vec<u32> = Vec::new();
        super::parse(input, &mut output, &mut stack).unwrap();
        return output;
    }

    #[test]
    fn parse_empty_array() {
        let result = parse(b"[]");
        assert_eq!(vec![
            Node {children: 0, length_in_bytes: 2},
        ], result);
    }

    #[test]
    fn parse_nested_arrays() {
        let result = parse(b"[[[]],[]]");
        assert_eq!(vec![
            Node {children: 3, length_in_bytes: 9},
            Node {children: 1, length_in_bytes: 4},
            Node {children: 0, length_in_bytes: 2},
            Node {children: 0, length_in_bytes: 2},
        ], result);
    }

    #[test]
    fn parse_nested_objects() {
        let result = parse(br#"{"A":{"B":{}},"C":{}}"#);
        assert_eq!(vec![
            Node {children: 6, length_in_bytes: 21},
            Node {children: 0, length_in_bytes: 3},
            Node {children: 2, length_in_bytes: 8},
            Node {children: 0, length_in_bytes: 3},
            Node {children: 0, length_in_bytes: 2},
            Node {children: 0, length_in_bytes: 3},
            Node {children: 0, length_in_bytes: 2},
        ], result);
    }

    #[test]
    fn parse_scalars() {
        let result = parse(br#"[false,null,true]"#);
        assert_eq!(vec![
            Node {children: 3, length_in_bytes: 17},
            Node {children: 0, length_in_bytes: 5},
            Node {children: 0, length_in_bytes: 4},
            Node {children: 0, length_in_bytes: 4},
        ], result);
    }
}
<commit_msg>Add some comments to the parsing code<commit_after>/* Copyright 2016 Mark Haines
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Parsed JSON is stored as a byte array of compact JSON and an array of nodes.
 * Each node represents a JSON object, array or value.
 *
 * The ``children`` field is the total number of children under this node.
 * The child nodes come directly after the parent node in the array.
 * This means that the next sibling of a node can be found at an offset
 * of ``children + 1`` into the array.
 *
 * The ``length_in_bytes`` gives the length of the value in bytes.
 * This allows the offset of a node to be computed as follows.
 * The root node is at offset 0.
 * The first child node starts at its parent offset + 1.
 * Each subsequent child node starts 1 byte after the end of previous node.
 *
 * JSON scalars never have children and are identified by their first byte.
 * * JSON true values start with ``b't' == 0x74``.
 * * JSON false values start with ``b'f' == 0x66``.
 * * JSON null values start with ``b'n' == 0x6E``.
 * * JSON string values start with ``b'"' == 0x22``.
 * * JSON number values start with either ``b'-' == 0x2B``
 *   or one of ``b"0123456789"` == [0x30..0x39]``.
 *
 * JSON arrays start with ``b'[' == 0x5B``. The direct children of the node
 *      are the elements of the array.
 *
 * JSON objects start with ``b'{' == 0x7B``. The direct children of the node
 * alternate between JSON string keys and their values.
 */
#[derive(PartialEq, Debug)]
pub struct Node {
    children: u32,
    length_in_bytes: u32,
}

pub fn parse(input: &[u8], output: &mut Vec<Node>, stack: &mut Vec<u32>) -> Result<(),()> {
    if parse_(input, output, stack) {
        Ok(())
    } else {
        Err(())
    }
}

fn parse_(input: &[u8], output: &mut Vec<Node>, stack: &mut Vec<u32>) -> bool {
    let mut iter = input.iter().peekable();
    let mut parsing_object = false;
    if iter.len() == 2 {
        // If the input is two bytes long then it is an empty object
        // or an empty array.
        output.push(Node {children: 0, length_in_bytes: 2});
        return true;
    }
    'node_end: loop {
        match stack.pop() {
            // On the first iteration of the loop the stack is empty.
            // So we fall through to start parsing the root array or
            // object.
            None => {},
            // Otherwise we've reached the end of an array or object.
            Some(offset_and_state) => {
                // Grab a copy of the length to appease the borrow checker.
                let output_len = output.len() as u32;
                let offset = offset_and_state >> 1;
                // The stack held the where the node was in the output array.
                // Grab a mutable copy of the node so we can fill it out with
                // the number of children and update the length of the node.
                // This lookup should never fail.
                let node = match output.get_mut(offset as usize) {
                    Some(node) => node,
                    None => return false
                };
                // The node length contined the number of bytes left in the
                // iterator when the node began. Subtracting the number of
                // bytes left now will give us the length.
                node.length_in_bytes -= iter.len() as u32;
                node.children = output_len - offset - 1;
                // Take a look at the next entry in the stack to get whether we
                // are parsing an array or parsing an object.
                // If the stack is empty then we've parsed the root object or
                // array and we can return.
                let prev_offset_and_state = match stack.last() {
                    Some(value) => value,
                    None => return true // <-- This is where the parser exits.
                };
                // Whether we were parsing an object or and array is stored in
                // the first bit of the stack entry.
                parsing_object = (prev_offset_and_state & 1) == 1;
                // We've finished parsing a node. There's either a comma b','
                // followed by more stuff in the outer node. or the outer node
                // is ending with a b']' or a b'}'.
                let input_char = match iter.next() {
                    None => return false,
                    Some(value) => *value
                };
                // If the node ends then jump to handling the end of a node.
                if input_char != b',' {
                    continue 'node_end;
                }
                // Otherwise fallthrough to parsing the start of a value.
            }
        };
        'value_start: loop {
            if parsing_object {
                // If we are parsing an object then parse the string key.
                let start = iter.len();
                // We can assume it's a string so skip the opening b'"'.
                if iter.next() == None {
                    return false;
                }
                // Loop through the characters until we find a closing b'"'.
                loop {
                    let input_char = match iter.next() {
                        None => return false,
                        Some(value) => *value
                    };
                    if input_char == b'"' {
                        break;
                    }
                    if input_char == b'\\' {
                        if iter.next() == None {
                            return false;
                        }
                    }
                }
                // Add a node with the string.
                output.push(Node {
                    children: 0, length_in_bytes: (start - iter.len()) as u32
                });
                // Skip over the b':'.
                if iter.next() == None {
                    return false;
                }
            }
            // Parse a JSON value.
            let start = iter.len();
            let input_char = match iter.next() {
                None => return false,
                Some(value) => *value
            };
            if input_char == b'{' {
                // This is the start of a JSON object.
                // Look at the next char to check if the object is empty.
                let peek_char = match iter.peek() {
                    None => return false,
                    Some(value) => **value
                };
                if peek_char == b'}' {
                    // The object was empty, output a 2 byte node.
                    output.push(Node {children: 0, length_in_bytes: 2});
                    // Consume the b'}' character.
                    let _ = iter.next();
                } else {
                    // The object is not empty.
                    // Add a placeholder node to the output vector recording
                    // where the start of the object was. The placeholder
                    // will be filled out with the correct info when the node
                    // ends.
                    // Add the index of the placeholder node in the output
                    // vector to the stack. Set the low bit to indicate we are
                    // parsing an object.
                    let index = output.len() as u32;
                    stack.push((index << 1) | 1);
                    output.push(Node {
                        children: 0, length_in_bytes: start as u32,
                    });
                    parsing_object = true;
                    // Jump to parsing the start of a value.
                    continue 'value_start;
                }
            } else if input_char == b'[' {
                // This is the start of a JSON array.
                // Look at the next char to check if the array is empty.
                let peek_char = match iter.peek() {
                    None => return false,
                    Some(value) => **value
                };
                if peek_char == b']' {
                    // The array is empty, output a 2 byte node.
                    output.push(Node {children: 0, length_in_bytes: 2});
                    // Consume the ']' character.
                    let _ = iter.next();
                } else {
                    // The array is not empty.
                    let index = output.len() as u32;
                    // Add a placeholder node to the output vector recording
                    // where the start of the array was. The placeholder
                    // will be filled out with the correct info when the node
                    // ends.
                    // Add the index of the placeholder node in the output
                    // vector to the stack. Leave the low bit unset to indicate
                    // we are parsing an array.
                    stack.push(index << 1);
                    output.push(Node {
                        children: 0, length_in_bytes: start as u32,
                    });
                    parsing_object = false;
                    // Jump to parsing the start of a value.
                    continue 'value_start;
                }
            } else if input_char == b'"' {
                // We are parsing a string. Loop until we see a closing b'"'.
                loop {
                    let input_char = match iter.next() {
                        None => return false,
                        Some(value) => *value
                    };
                    if input_char == b'"' {
                        break;
                    }
                    if input_char == b'\\' {
                        if iter.next() == None {
                            return false;
                        }
                    }
                }
                output.push(Node {
                    children: 0,
                    length_in_bytes: (start - iter.len()) as u32
                });
            } else {
                // We are parsing a number or one of true, false or null.
                // Loop until we see a b',', a b'}', or a b']'.
                loop {
                    let input_char = match iter.next() {
                        None => return false,
                        Some(value) => *value
                    };
                    if input_char == b',' {
                        output.push(Node {
                            children: 0,
                            length_in_bytes: (start - iter.len() - 1) as u32
                        });
                        // Jump to parsing the start of a value.
                        continue 'value_start;
                    } else if (input_char & 0xDF) == b']' {
                        output.push(Node {
                            children: 0,
                            length_in_bytes: (start - iter.len() - 1) as u32
                        });
                        // Jump to parsing the end of a node.
                        continue 'node_end;
                    }
                }
            }
            // Strings, empty objects and empty arrays fall through to here to
            // handle the end of a value.
            // The next character is either a b',' if there is another value
            // to parse in the containing object or array or the character
            // is a b']' or a b'}' if the contaning object or array is ending.
            let input_char = match iter.next() {
                None => return false,
                Some(value) => *value
            };
            if input_char == b',' {
                // Jump to parsing the start of a value.
                continue 'value_start;
            } else {
                // Jump to parsing the end of a node.
                continue 'node_end;
            }
        }
    }
}

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

    fn parse(input: &[u8]) -> Vec<Node> {
        let mut output : Vec<Node> = Vec::new();
        let mut stack : Vec<u32> = Vec::new();
        super::parse(input, &mut output, &mut stack).unwrap();
        return output;
    }

    #[test]
    fn parse_empty_array() {
        let result = parse(b"[]");
        assert_eq!(vec![
            Node {children: 0, length_in_bytes: 2},
        ], result);
    }

    #[test]
    fn parse_nested_arrays() {
        let result = parse(b"[[[]],[]]");
        assert_eq!(vec![
            Node {children: 3, length_in_bytes: 9},
            Node {children: 1, length_in_bytes: 4},
            Node {children: 0, length_in_bytes: 2},
            Node {children: 0, length_in_bytes: 2},
        ], result);
    }

    #[test]
    fn parse_nested_objects() {
        let result = parse(br#"{"A":{"B":{}},"C":{}}"#);
        assert_eq!(vec![
            Node {children: 6, length_in_bytes: 21},
            Node {children: 0, length_in_bytes: 3},
            Node {children: 2, length_in_bytes: 8},
            Node {children: 0, length_in_bytes: 3},
            Node {children: 0, length_in_bytes: 2},
            Node {children: 0, length_in_bytes: 3},
            Node {children: 0, length_in_bytes: 2},
        ], result);
    }

    #[test]
    fn parse_scalars() {
        let result = parse(br#"[false,null,true]"#);
        assert_eq!(vec![
            Node {children: 3, length_in_bytes: 17},
            Node {children: 0, length_in_bytes: 5},
            Node {children: 0, length_in_bytes: 4},
            Node {children: 0, length_in_bytes: 4},
        ], result);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]

use util::*;

/// Plane-specific configuration.
#[derive(Debug, Clone)]
pub struct PlaneConfig {
  pub stride: usize,
  pub alloc_height: usize,
  pub width: usize,
  pub height: usize,
  pub xdec: usize,
  pub ydec: usize,
  pub xorigin: isize,
  pub yorigin: isize
}

/// Absolute offset in pixels inside a plane
#[derive(Debug)]
pub struct PlaneOffset {
  pub x: isize,
  pub y: isize
}

#[derive(Debug, Clone)]
pub struct Plane {
  pub data: Vec<u16>,
  pub cfg: PlaneConfig
}

impl Plane {
  /// Stride alignment in bytes.
  const STRIDE_ALIGNMENT_LOG2: usize = 4;

  /// Data alignment in bytes.
  const DATA_ALIGNMENT_LOG2: usize = 4;

  pub fn new(
    width: usize, height: usize, xdec: usize, ydec: usize, xpad: usize,
    ypad: usize
  ) -> Plane {
    let xorigin =
      xpad.align_power_of_two(Plane::STRIDE_ALIGNMENT_LOG2 - 1) as isize;
    let yorigin = ypad as isize;
    let stride = (xorigin as usize + width + xpad)
      .align_power_of_two(Plane::STRIDE_ALIGNMENT_LOG2 - 1);
    let alloc_height = yorigin as usize + height + ypad;
    let data = vec![128u16; stride * alloc_height];
    assert!(is_aligned(data.as_ptr(), Plane::DATA_ALIGNMENT_LOG2));
    Plane {
      data,
      cfg: PlaneConfig {
        stride,
        alloc_height,
        width,
        height,
        xdec,
        ydec,
        xorigin,
        yorigin
      }
    }
  }

  pub fn window(&self, po: &PlaneOffset) -> Plane {
    assert!(self.cfg.xorigin >= 0 && self.cfg.yorigin >= 0);
    let x = po.x as usize;
    let y = po.y as usize;
    let xpad = self.cfg.xorigin as usize;
    let ypad = self.cfg.yorigin as usize;
    let xdec = self.cfg.xdec;
    let ydec = self.cfg.ydec;
    let xorigin = self.cfg.xorigin - po.x;
    let yorigin = self.cfg.yorigin - po.y;
    let width = 1 << (6 - xdec);
    let height = 1 << (6 - ydec);
    let stride = (xpad + width + xpad)
      .align_power_of_two(Plane::STRIDE_ALIGNMENT_LOG2 - 1);
    let alloc_height = ypad + height + ypad;
    let mut data = vec![128u16; stride * alloc_height];
    for (d, s) in data
      .chunks_mut(stride)
      .zip(self.data[(y * self.cfg.stride)..].chunks(self.cfg.stride))
    {
      let w = d.len().min(s.len() - x);
      d[..w].copy_from_slice(&s[x..(x + w)]);
    }
    Plane {
      data,
      cfg: PlaneConfig {
        stride,
        alloc_height,
        width,
        height,
        xdec,
        ydec,
        xorigin,
        yorigin
      }
    }
  }

  pub fn pad(&mut self) {
    assert!(self.cfg.xorigin >= 0 && self.cfg.yorigin >= 0);
    let xorigin = self.cfg.xorigin as usize;
    let yorigin = self.cfg.yorigin as usize;
    let stride = self.cfg.stride;
    let width = self.cfg.width;
    let height = self.cfg.height;

    if xorigin > 0 {
      for y in 0..height {
        let mut ps = self
          .mut_slice(&PlaneOffset { x: -(xorigin as isize), y: y as isize });
        let s = ps.as_mut_slice_w_width(xorigin + 1);
        let fill_val = s[xorigin];
        for val in s[..xorigin].iter_mut() {
          *val = fill_val;
        }
      }
    }

    if xorigin + width < stride {
      for y in 0..height {
        let mut ps = self
          .mut_slice(&PlaneOffset { x: width as isize - 1, y: y as isize });
        let s = ps.as_mut_slice_w_width(stride - xorigin - width + 1);
        let fill_val = s[0];
        for val in s[1..].iter_mut() {
          *val = fill_val;
        }
      }
    }

    if yorigin > 0 {
      let mut ps = self.mut_slice(&PlaneOffset {
        x: -(xorigin as isize),
        y: -(yorigin as isize)
      });
      let (s1, s2) = ps.as_mut_slice().split_at_mut(yorigin * stride);
      for y in 0..yorigin {
        s1[y * stride..y * stride + stride].copy_from_slice(&s2[..stride]);
      }
    }

    if yorigin + height < self.cfg.alloc_height {
      let mut ps = self.mut_slice(&PlaneOffset {
        x: -(xorigin as isize),
        y: height as isize - 1
      });
      let (s2, s1) = ps.as_mut_slice().split_at_mut(stride);
      for y in 0..yorigin {
        s1[y * stride..y * stride + stride].copy_from_slice(&s2[..stride]);
      }
    }
  }

  pub fn slice<'a>(&'a self, po: &PlaneOffset) -> PlaneSlice<'a> {
    PlaneSlice { plane: self, x: po.x, y: po.y }
  }

  pub fn mut_slice<'a>(&'a mut self, po: &PlaneOffset) -> PlaneMutSlice<'a> {
    PlaneMutSlice { plane: self, x: po.x, y: po.y }
  }

  #[inline]
  fn index(&self, x: usize, y: usize) -> usize {
    let i = (y as isize + self.cfg.yorigin) * self.cfg.stride as isize
      + (x as isize + self.cfg.xorigin);
    assert!(i >= 0);
    i as usize
  }

  pub fn p(&self, x: usize, y: usize) -> u16 {
    self.data[self.index(x, y)]
  }

  pub fn data_origin(&self) -> &[u16] {
    &self.data[self.index(0, 0)..]
  }

  pub fn data_origin_mut(&mut self) -> &mut [u16] {
    let i = self.index(0, 0);
    &mut self.data[i..]
  }

  pub fn copy_from_raw_u8(
    &mut self, source: &[u8], source_stride: usize, source_bytewidth: usize
  ) {
    let stride = self.cfg.stride;
    for (self_row, source_row) in self
      .data_origin_mut()
      .chunks_mut(stride)
      .zip(source.chunks(source_stride))
    {
      match source_bytewidth {
        1 => for (self_pixel, source_pixel) in
          self_row.iter_mut().zip(source_row.iter())
        {
          *self_pixel = *source_pixel as u16;
        },
        2 => for (self_pixel, bytes) in
          self_row.iter_mut().zip(source_row.chunks(2))
        {
          *self_pixel = (bytes[1] as u16) << 8 | (bytes[0] as u16);
        },

        _ => {}
      }
    }
  }
}

#[derive(Clone, Copy)]
pub struct PlaneSlice<'a> {
  pub plane: &'a Plane,
  pub x: isize,
  pub y: isize
}

pub struct IterWidth<'a> {
    ps: PlaneSlice<'a>,
    width: usize,
}

// TODO: Implement more methods
impl<'a> Iterator for IterWidth<'a> {
    type Item = &'a [u16];

    #[inline]
    fn next(&mut self) -> Option<&'a [u16]> {
        let x = self.ps.plane.cfg.xorigin + self.ps.x;
        let y = self.ps.plane.cfg.yorigin + self.ps.y;
        let stride = self.ps.plane.cfg.stride;
        let base = y as usize * stride + x as usize;

        if self.ps.plane.data.len() < base + self.width {
            None
        } else {
            self.ps.y += 1;
            Some(&self.ps.plane.data[base..base + self.width])
        }
    }
}

impl<'a> PlaneSlice<'a> {
  pub fn as_slice(&'a self) -> &'a [u16] {
    let stride = self.plane.cfg.stride;
    let base = (self.y + self.plane.cfg.yorigin) as usize * stride
      + (self.x + self.plane.cfg.xorigin) as usize;
    &self.plane.data[base..]
  }

  pub fn as_slice_clamped(&'a self) -> &'a [u16] {
    let stride = self.plane.cfg.stride;
    let y = (self.y.min(self.plane.cfg.height as isize)
      + self.plane.cfg.yorigin)
      .max(0) as usize;
    let x = (self.x.min(self.plane.cfg.width as isize)
      + self.plane.cfg.xorigin)
      .max(0) as usize;
    &self.plane.data[y * stride + x..]
  }

  pub fn as_slice_w_width(&'a self, width: usize) -> &'a [u16] {
    let stride = self.plane.cfg.stride;
    let base = (self.y + self.plane.cfg.yorigin) as usize * stride
      + (self.x + self.plane.cfg.xorigin) as usize;
    &self.plane.data[base..base + width]
  }

  pub fn iter_width(&self, width: usize) -> IterWidth<'a> {
    IterWidth { ps: *self, width }
  }

  pub fn subslice(&'a self, xo: usize, yo: usize) -> PlaneSlice<'a> {
    PlaneSlice {
      plane: self.plane,
      x: self.x + xo as isize,
      y: self.y + yo as isize
    }
  }

  /// A slice starting i pixels above the current one.
  pub fn go_up(&'a self, i: usize) -> PlaneSlice<'a> {
    PlaneSlice { plane: self.plane, x: self.x, y: self.y - i as isize }
  }

  /// A slice starting i pixels to the left of the current one.
  pub fn go_left(&'a self, i: usize) -> PlaneSlice<'a> {
    PlaneSlice { plane: self.plane, x: self.x - i as isize, y: self.y }
  }

  pub fn p(&self, add_x: usize, add_y: usize) -> u16 {
    let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin) as usize;
    let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin) as usize;
    self.plane.data[new_y * self.plane.cfg.stride + new_x]
  }
}

pub struct PlaneMutSlice<'a> {
  pub plane: &'a mut Plane,
  pub x: isize,
  pub y: isize
}

impl<'a> PlaneMutSlice<'a> {
  pub fn as_mut_slice(&'a mut self) -> &'a mut [u16] {
    let stride = self.plane.cfg.stride;
    let base = (self.y + self.plane.cfg.yorigin) as usize * stride
      + (self.x + self.plane.cfg.xorigin) as usize;
    &mut self.plane.data[base..]
  }

  pub fn as_mut_slice_w_width(&'a mut self, width: usize) -> &'a mut [u16] {
    let stride = self.plane.cfg.stride;
    let y = self.y + self.plane.cfg.yorigin;
    let x = self.x + self.plane.cfg.xorigin;
    assert!(y >= 0);
    assert!(x >= 0);
    let base = y as usize * stride + x as usize;
    &mut self.plane.data[base..base + width]
  }

  pub fn offset(&self, add_x: usize, add_y: usize) -> &[u16] {
    let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin) as usize;
    let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin) as usize;
    &self.plane.data[new_y * self.plane.cfg.stride + new_x..]
  }

  pub fn offset_as_mutable(
    &'a mut self, add_x: usize, add_y: usize
  ) -> &'a mut [u16] {
    let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin) as usize;
    let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin) as usize;
    &mut self.plane.data[new_y * self.plane.cfg.stride + new_x..]
  }

  // FIXME: code duplication with PlaneSlice

  /// A slice starting i pixels above the current one.
  pub fn go_up(&'a self, i: usize) -> PlaneSlice<'a> {
    PlaneSlice { plane: self.plane, x: self.x, y: self.y - i as isize }
  }

  /// A slice starting i pixels to the left of the current one.
  pub fn go_left(&'a self, i: usize) -> PlaneSlice<'a> {
    PlaneSlice { plane: self.plane, x: self.x - i as isize, y: self.y }
  }

  pub fn p(&self, add_x: usize, add_y: usize) -> u16 {
    let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin) as usize;
    let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin) as usize;
    self.plane.data[new_y * self.plane.cfg.stride + new_x]
  }
}
<commit_msg>Extend the implementation of IterWidth<commit_after>// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]

use util::*;

/// Plane-specific configuration.
#[derive(Debug, Clone)]
pub struct PlaneConfig {
  pub stride: usize,
  pub alloc_height: usize,
  pub width: usize,
  pub height: usize,
  pub xdec: usize,
  pub ydec: usize,
  pub xorigin: isize,
  pub yorigin: isize
}

/// Absolute offset in pixels inside a plane
#[derive(Debug)]
pub struct PlaneOffset {
  pub x: isize,
  pub y: isize
}

#[derive(Debug, Clone)]
pub struct Plane {
  pub data: Vec<u16>,
  pub cfg: PlaneConfig
}

impl Plane {
  /// Stride alignment in bytes.
  const STRIDE_ALIGNMENT_LOG2: usize = 4;

  /// Data alignment in bytes.
  const DATA_ALIGNMENT_LOG2: usize = 4;

  pub fn new(
    width: usize, height: usize, xdec: usize, ydec: usize, xpad: usize,
    ypad: usize
  ) -> Plane {
    let xorigin =
      xpad.align_power_of_two(Plane::STRIDE_ALIGNMENT_LOG2 - 1) as isize;
    let yorigin = ypad as isize;
    let stride = (xorigin as usize + width + xpad)
      .align_power_of_two(Plane::STRIDE_ALIGNMENT_LOG2 - 1);
    let alloc_height = yorigin as usize + height + ypad;
    let data = vec![128u16; stride * alloc_height];
    assert!(is_aligned(data.as_ptr(), Plane::DATA_ALIGNMENT_LOG2));
    Plane {
      data,
      cfg: PlaneConfig {
        stride,
        alloc_height,
        width,
        height,
        xdec,
        ydec,
        xorigin,
        yorigin
      }
    }
  }

  pub fn window(&self, po: &PlaneOffset) -> Plane {
    assert!(self.cfg.xorigin >= 0 && self.cfg.yorigin >= 0);
    let x = po.x as usize;
    let y = po.y as usize;
    let xpad = self.cfg.xorigin as usize;
    let ypad = self.cfg.yorigin as usize;
    let xdec = self.cfg.xdec;
    let ydec = self.cfg.ydec;
    let xorigin = self.cfg.xorigin - po.x;
    let yorigin = self.cfg.yorigin - po.y;
    let width = 1 << (6 - xdec);
    let height = 1 << (6 - ydec);
    let stride = (xpad + width + xpad)
      .align_power_of_two(Plane::STRIDE_ALIGNMENT_LOG2 - 1);
    let alloc_height = ypad + height + ypad;
    let mut data = vec![128u16; stride * alloc_height];
    for (d, s) in data
      .chunks_mut(stride)
      .zip(self.data[(y * self.cfg.stride)..].chunks(self.cfg.stride))
    {
      let w = d.len().min(s.len() - x);
      d[..w].copy_from_slice(&s[x..(x + w)]);
    }
    Plane {
      data,
      cfg: PlaneConfig {
        stride,
        alloc_height,
        width,
        height,
        xdec,
        ydec,
        xorigin,
        yorigin
      }
    }
  }

  pub fn pad(&mut self) {
    assert!(self.cfg.xorigin >= 0 && self.cfg.yorigin >= 0);
    let xorigin = self.cfg.xorigin as usize;
    let yorigin = self.cfg.yorigin as usize;
    let stride = self.cfg.stride;
    let width = self.cfg.width;
    let height = self.cfg.height;

    if xorigin > 0 {
      for y in 0..height {
        let mut ps = self
          .mut_slice(&PlaneOffset { x: -(xorigin as isize), y: y as isize });
        let s = ps.as_mut_slice_w_width(xorigin + 1);
        let fill_val = s[xorigin];
        for val in s[..xorigin].iter_mut() {
          *val = fill_val;
        }
      }
    }

    if xorigin + width < stride {
      for y in 0..height {
        let mut ps = self
          .mut_slice(&PlaneOffset { x: width as isize - 1, y: y as isize });
        let s = ps.as_mut_slice_w_width(stride - xorigin - width + 1);
        let fill_val = s[0];
        for val in s[1..].iter_mut() {
          *val = fill_val;
        }
      }
    }

    if yorigin > 0 {
      let mut ps = self.mut_slice(&PlaneOffset {
        x: -(xorigin as isize),
        y: -(yorigin as isize)
      });
      let (s1, s2) = ps.as_mut_slice().split_at_mut(yorigin * stride);
      for y in 0..yorigin {
        s1[y * stride..y * stride + stride].copy_from_slice(&s2[..stride]);
      }
    }

    if yorigin + height < self.cfg.alloc_height {
      let mut ps = self.mut_slice(&PlaneOffset {
        x: -(xorigin as isize),
        y: height as isize - 1
      });
      let (s2, s1) = ps.as_mut_slice().split_at_mut(stride);
      for y in 0..yorigin {
        s1[y * stride..y * stride + stride].copy_from_slice(&s2[..stride]);
      }
    }
  }

  pub fn slice<'a>(&'a self, po: &PlaneOffset) -> PlaneSlice<'a> {
    PlaneSlice { plane: self, x: po.x, y: po.y }
  }

  pub fn mut_slice<'a>(&'a mut self, po: &PlaneOffset) -> PlaneMutSlice<'a> {
    PlaneMutSlice { plane: self, x: po.x, y: po.y }
  }

  #[inline]
  fn index(&self, x: usize, y: usize) -> usize {
    let i = (y as isize + self.cfg.yorigin) * self.cfg.stride as isize
      + (x as isize + self.cfg.xorigin);
    assert!(i >= 0);
    i as usize
  }

  pub fn p(&self, x: usize, y: usize) -> u16 {
    self.data[self.index(x, y)]
  }

  pub fn data_origin(&self) -> &[u16] {
    &self.data[self.index(0, 0)..]
  }

  pub fn data_origin_mut(&mut self) -> &mut [u16] {
    let i = self.index(0, 0);
    &mut self.data[i..]
  }

  pub fn copy_from_raw_u8(
    &mut self, source: &[u8], source_stride: usize, source_bytewidth: usize
  ) {
    let stride = self.cfg.stride;
    for (self_row, source_row) in self
      .data_origin_mut()
      .chunks_mut(stride)
      .zip(source.chunks(source_stride))
    {
      match source_bytewidth {
        1 => for (self_pixel, source_pixel) in
          self_row.iter_mut().zip(source_row.iter())
        {
          *self_pixel = *source_pixel as u16;
        },
        2 => for (self_pixel, bytes) in
          self_row.iter_mut().zip(source_row.chunks(2))
        {
          *self_pixel = (bytes[1] as u16) << 8 | (bytes[0] as u16);
        },

        _ => {}
      }
    }
  }
}

#[derive(Clone, Copy)]
pub struct PlaneSlice<'a> {
  pub plane: &'a Plane,
  pub x: isize,
  pub y: isize
}

pub struct IterWidth<'a> {
    ps: PlaneSlice<'a>,
    width: usize,
}

impl<'a> Iterator for IterWidth<'a> {
    type Item = &'a [u16];

    #[inline]
    fn next(&mut self) -> Option<&'a [u16]> {
        let x = self.ps.plane.cfg.xorigin + self.ps.x;
        let y = self.ps.plane.cfg.yorigin + self.ps.y;
        let stride = self.ps.plane.cfg.stride;
        let base = y as usize * stride + x as usize;

        if self.ps.plane.data.len() < base + self.width {
            None
        } else {
            self.ps.y += 1;
            Some(&self.ps.plane.data[base..base + self.width])
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let size = self.ps.plane.cfg.height - self.ps.y as usize;

        (size, Some(size))
    }
}

impl<'a> ExactSizeIterator for IterWidth<'a> { }

use std::iter::FusedIterator;
impl<'a> FusedIterator for IterWidth<'a> { }

impl<'a> PlaneSlice<'a> {
  pub fn as_slice(&'a self) -> &'a [u16] {
    let stride = self.plane.cfg.stride;
    let base = (self.y + self.plane.cfg.yorigin) as usize * stride
      + (self.x + self.plane.cfg.xorigin) as usize;
    &self.plane.data[base..]
  }

  pub fn as_slice_clamped(&'a self) -> &'a [u16] {
    let stride = self.plane.cfg.stride;
    let y = (self.y.min(self.plane.cfg.height as isize)
      + self.plane.cfg.yorigin)
      .max(0) as usize;
    let x = (self.x.min(self.plane.cfg.width as isize)
      + self.plane.cfg.xorigin)
      .max(0) as usize;
    &self.plane.data[y * stride + x..]
  }

  pub fn as_slice_w_width(&'a self, width: usize) -> &'a [u16] {
    let stride = self.plane.cfg.stride;
    let base = (self.y + self.plane.cfg.yorigin) as usize * stride
      + (self.x + self.plane.cfg.xorigin) as usize;
    &self.plane.data[base..base + width]
  }

  pub fn iter_width(&self, width: usize) -> IterWidth<'a> {
    IterWidth { ps: *self, width }
  }

  pub fn subslice(&'a self, xo: usize, yo: usize) -> PlaneSlice<'a> {
    PlaneSlice {
      plane: self.plane,
      x: self.x + xo as isize,
      y: self.y + yo as isize
    }
  }

  /// A slice starting i pixels above the current one.
  pub fn go_up(&'a self, i: usize) -> PlaneSlice<'a> {
    PlaneSlice { plane: self.plane, x: self.x, y: self.y - i as isize }
  }

  /// A slice starting i pixels to the left of the current one.
  pub fn go_left(&'a self, i: usize) -> PlaneSlice<'a> {
    PlaneSlice { plane: self.plane, x: self.x - i as isize, y: self.y }
  }

  pub fn p(&self, add_x: usize, add_y: usize) -> u16 {
    let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin) as usize;
    let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin) as usize;
    self.plane.data[new_y * self.plane.cfg.stride + new_x]
  }
}

pub struct PlaneMutSlice<'a> {
  pub plane: &'a mut Plane,
  pub x: isize,
  pub y: isize
}

impl<'a> PlaneMutSlice<'a> {
  pub fn as_mut_slice(&'a mut self) -> &'a mut [u16] {
    let stride = self.plane.cfg.stride;
    let base = (self.y + self.plane.cfg.yorigin) as usize * stride
      + (self.x + self.plane.cfg.xorigin) as usize;
    &mut self.plane.data[base..]
  }

  pub fn as_mut_slice_w_width(&'a mut self, width: usize) -> &'a mut [u16] {
    let stride = self.plane.cfg.stride;
    let y = self.y + self.plane.cfg.yorigin;
    let x = self.x + self.plane.cfg.xorigin;
    assert!(y >= 0);
    assert!(x >= 0);
    let base = y as usize * stride + x as usize;
    &mut self.plane.data[base..base + width]
  }

  pub fn offset(&self, add_x: usize, add_y: usize) -> &[u16] {
    let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin) as usize;
    let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin) as usize;
    &self.plane.data[new_y * self.plane.cfg.stride + new_x..]
  }

  pub fn offset_as_mutable(
    &'a mut self, add_x: usize, add_y: usize
  ) -> &'a mut [u16] {
    let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin) as usize;
    let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin) as usize;
    &mut self.plane.data[new_y * self.plane.cfg.stride + new_x..]
  }

  // FIXME: code duplication with PlaneSlice

  /// A slice starting i pixels above the current one.
  pub fn go_up(&'a self, i: usize) -> PlaneSlice<'a> {
    PlaneSlice { plane: self.plane, x: self.x, y: self.y - i as isize }
  }

  /// A slice starting i pixels to the left of the current one.
  pub fn go_left(&'a self, i: usize) -> PlaneSlice<'a> {
    PlaneSlice { plane: self.plane, x: self.x - i as isize, y: self.y }
  }

  pub fn p(&self, add_x: usize, add_y: usize) -> u16 {
    let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin) as usize;
    let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin) as usize;
    self.plane.data[new_y * self.plane.cfg.stride + new_x]
  }
}
<|endoftext|>


<commit_before>// Copyright 2014 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.

/*!
Syntax extension to generate FourCCs.

Once loaded, fourcc!() is called with a single 4-character string,
and an optional ident that is either `big`, `little`, or `target`.
The ident represents endianness, and specifies in which direction
the characters should be read. If the ident is omitted, it is assumed
to be `big`, i.e. left-to-right order. It returns a u32.

# Examples

To load the extension and use it:

```rust,ignore
#[phase(syntax)]
extern mod fourcc;

fn main() {
    let val = fourcc!("\xC0\xFF\xEE!")
    // val is 0xC0FFEE21
    let big_val = fourcc!("foo ", big);
    // big_val is 0x21EEFFC0
}
 ```

# References

* [Wikipedia: FourCC](http://en.wikipedia.org/wiki/FourCC)

*/

#[crate_id = "fourcc#0.10-pre"];
#[crate_type = "rlib"];
#[crate_type = "dylib"];
#[license = "MIT/ASL2"];

#[feature(macro_registrar, managed_boxes)];

extern mod syntax;

use syntax::ast;
use syntax::ast::Name;
use syntax::attr::contains;
use syntax::codemap::{Span, mk_sp};
use syntax::ext::base;
use syntax::ext::base::{SyntaxExtension, BasicMacroExpander, NormalTT, ExtCtxt, MRExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::InternedString;

#[macro_registrar]
#[cfg(not(test))]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("fourcc"),
        NormalTT(~BasicMacroExpander {
            expander: expand_syntax_ext,
            span: None,
        },
        None));
}

use std::ascii::AsciiCast;

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult {
    let (expr, endian) = parse_tts(cx, tts);

    let little = match endian {
        None => false,
        Some(Ident{ident, span}) => match token::get_ident(ident.name).get() {
            "little" => true,
            "big" => false,
            "target" => target_endian_little(cx, sp),
            _ => {
                cx.span_err(span, "invalid endian directive in fourcc!");
                target_endian_little(cx, sp)
            }
        }
    };

    let s = match expr.node {
        // expression is a literal
        ast::ExprLit(lit) => match lit.node {
            // string literal
            ast::LitStr(ref s, _) => {
                if !s.get().is_ascii() {
                    cx.span_err(expr.span, "non-ascii string literal in fourcc!");
                } else if s.get().len() != 4 {
                    cx.span_err(expr.span, "string literal with len != 4 in fourcc!");
                }
                s
            }
            _ => {
                cx.span_err(expr.span, "unsupported literal in fourcc!");
                return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32)));
            }
        },
        _ => {
            cx.span_err(expr.span, "non-literal in fourcc!");
            return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32)));
        }
    };

    let mut val = 0u32;
    if little {
        for byte in s.get().bytes_rev().take(4) {
            val = (val << 8) | (byte as u32);
        }
    } else {
        for byte in s.get().bytes().take(4) {
            val = (val << 8) | (byte as u32);
        }
    }
    let e = cx.expr_lit(sp, ast::LitUint(val as u64, ast::TyU32));
    MRExpr(e)
}

struct Ident {
    ident: ast::Ident,
    span: Span
}

fn parse_tts(cx: &ExtCtxt, tts: &[ast::TokenTree]) -> (@ast::Expr, Option<Ident>) {
    let p = &mut parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_owned());
    let ex = p.parse_expr();
    let id = if p.token == token::EOF {
        None
    } else {
        p.expect(&token::COMMA);
        let lo = p.span.lo;
        let ident = p.parse_ident();
        let hi = p.last_span.hi;
        Some(Ident{ident: ident, span: mk_sp(lo, hi)})
    };
    if p.token != token::EOF {
        p.unexpected();
    }
    (ex, id)
}

fn target_endian_little(cx: &ExtCtxt, sp: Span) -> bool {
    let meta = cx.meta_name_value(sp, InternedString::new("target_endian"),
        ast::LitStr(InternedString::new("little"), ast::CookedStr));
    contains(cx.cfg(), meta)
}

// Fixes LLVM assert on Windows
#[test]
fn dummy_test() { }
<commit_msg>Allow codepoints 128-255 in fourc!!<commit_after>// Copyright 2014 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.

/*!
Syntax extension to generate FourCCs.

Once loaded, fourcc!() is called with a single 4-character string,
and an optional ident that is either `big`, `little`, or `target`.
The ident represents endianness, and specifies in which direction
the characters should be read. If the ident is omitted, it is assumed
to be `big`, i.e. left-to-right order. It returns a u32.

# Examples

To load the extension and use it:

```rust,ignore
#[phase(syntax)]
extern mod fourcc;

fn main() {
    let val = fourcc!("\xC0\xFF\xEE!")
    // val is 0xC0FFEE21
    let big_val = fourcc!("foo ", big);
    // big_val is 0x21EEFFC0
}
 ```

# References

* [Wikipedia: FourCC](http://en.wikipedia.org/wiki/FourCC)

*/

#[crate_id = "fourcc#0.10-pre"];
#[crate_type = "rlib"];
#[crate_type = "dylib"];
#[license = "MIT/ASL2"];

#[feature(macro_registrar, managed_boxes)];

extern mod syntax;

use syntax::ast;
use syntax::ast::Name;
use syntax::attr::contains;
use syntax::codemap::{Span, mk_sp};
use syntax::ext::base;
use syntax::ext::base::{SyntaxExtension, BasicMacroExpander, NormalTT, ExtCtxt, MRExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::InternedString;

#[macro_registrar]
#[cfg(not(test))]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("fourcc"),
        NormalTT(~BasicMacroExpander {
            expander: expand_syntax_ext,
            span: None,
        },
        None));
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult {
    let (expr, endian) = parse_tts(cx, tts);

    let little = match endian {
        None => false,
        Some(Ident{ident, span}) => match token::get_ident(ident.name).get() {
            "little" => true,
            "big" => false,
            "target" => target_endian_little(cx, sp),
            _ => {
                cx.span_err(span, "invalid endian directive in fourcc!");
                target_endian_little(cx, sp)
            }
        }
    };

    let s = match expr.node {
        // expression is a literal
        ast::ExprLit(lit) => match lit.node {
            // string literal
            ast::LitStr(ref s, _) => {
                if s.get().char_len() != 4 {
                    cx.span_err(expr.span, "string literal with len != 4 in fourcc!");
                }
                s
            }
            _ => {
                cx.span_err(expr.span, "unsupported literal in fourcc!");
                return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32)));
            }
        },
        _ => {
            cx.span_err(expr.span, "non-literal in fourcc!");
            return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32)));
        }
    };

    let mut val = 0u32;
    for codepoint in s.get().chars().take(4) {
        let byte = if codepoint as u32 > 0xFF {
            cx.span_err(expr.span, "fourcc! literal character out of range 0-255");
            0u8
        } else {
            codepoint as u8
        };

        val = if little {
            (val >> 8) | ((byte as u32) << 24)
        } else {
            (val << 8) | (byte as u32)
        };
    }
    let e = cx.expr_lit(sp, ast::LitUint(val as u64, ast::TyU32));
    MRExpr(e)
}

struct Ident {
    ident: ast::Ident,
    span: Span
}

fn parse_tts(cx: &ExtCtxt, tts: &[ast::TokenTree]) -> (@ast::Expr, Option<Ident>) {
    let p = &mut parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_owned());
    let ex = p.parse_expr();
    let id = if p.token == token::EOF {
        None
    } else {
        p.expect(&token::COMMA);
        let lo = p.span.lo;
        let ident = p.parse_ident();
        let hi = p.last_span.hi;
        Some(Ident{ident: ident, span: mk_sp(lo, hi)})
    };
    if p.token != token::EOF {
        p.unexpected();
    }
    (ex, id)
}

fn target_endian_little(cx: &ExtCtxt, sp: Span) -> bool {
    let meta = cx.meta_name_value(sp, InternedString::new("target_endian"),
        ast::LitStr(InternedString::new("little"), ast::CookedStr));
    contains(cx.cfg(), meta)
}

// Fixes LLVM assert on Windows
#[test]
fn dummy_test() { }
<|endoftext|>


<commit_before>use glib::object::Cast;
use gtk::{self, Type};
use gtk::{
    BoxExt, CellLayoutExt, CellRendererExt, ContainerExt, EntryExt, GridExt,
    GtkListStoreExtManual, OverlayExt, SearchBarExt, TreeModelExt, TreeModelFilterExt,
    TreeSelectionExt, TreeViewColumnExt, TreeViewExt, WidgetExt,
};

use sysinfo::*;

use notebook::NoteBook;

use std::cell::Cell;
use std::collections::HashMap;
use std::rc::Rc;

#[allow(dead_code)]
pub struct Procs {
    pub left_tree: gtk::TreeView,
    pub scroll: gtk::ScrolledWindow,
    pub current_pid: Rc<Cell<Option<Pid>>>,
    pub kill_button: gtk::Button,
    pub info_button: gtk::Button,
    pub vertical_layout: gtk::Box,
    pub list_store: gtk::ListStore,
    pub columns: Vec<gtk::TreeViewColumn>,
    pub filter_entry: gtk::Entry,
    pub search_bar: gtk::SearchBar,
}

impl Procs {
    pub fn new(proc_list: &HashMap<Pid, Process>, note: &mut NoteBook) -> Procs {
        let left_tree = gtk::TreeView::new();
        let scroll = gtk::ScrolledWindow::new(None::<&gtk::Adjustment>, None::<&gtk::Adjustment>);
        let current_pid = Rc::new(Cell::new(None));
        let kill_button = gtk::Button::new_with_label("End task");
        let info_button = gtk::Button::new_with_label("More information");

        let overlay = gtk::Overlay::new();
        let filter_entry = gtk::Entry::new();
        let search_bar = gtk::SearchBar::new();

        // We put the filter entry at the right bottom.
        filter_entry.set_halign(gtk::Align::End);
        filter_entry.set_valign(gtk::Align::End);
        filter_entry.hide(); // By default, we don't show it.
        search_bar.connect_entry(&filter_entry);
        search_bar.set_show_close_button(true);

        overlay.add_overlay(&filter_entry);

        let mut columns: Vec<gtk::TreeViewColumn> = Vec::new();

        let list_store = gtk::ListStore::new(&[
            // The first four columns of the model are going to be visible in the view.
            Type::U32,       // pid
            Type::String,    // name
            Type::String,    // CPU
            Type::U64,       // mem
            // These two will serve as keys when sorting by process name and CPU usage.
            Type::String,    // name_lowercase
            Type::F32,       // CPU_f32
        ]);

        append_column("pid", &mut columns, &left_tree, None);
        append_column("process name", &mut columns, &left_tree, Some(200));
        append_column("cpu usage", &mut columns, &left_tree, None);
        append_column("memory usage (in kB)", &mut columns, &left_tree, None);

        // When we click the "name" column the order is defined by the
        // "name_lowercase" effectively making the built-in comparator ignore case.
        columns[1].set_sort_column_id(4);
        // Likewise clicking the "CPU" column sorts by the "CPU_f32" one because
        // we want the order to be numerical not lexicographical.
        columns[2].set_sort_column_id(5);

        for pro in proc_list.values() {
            create_and_fill_model(&list_store, pro.pid().as_u32(), &format!("{:?}", &pro.cmd()),
                                  &pro.name(), pro.cpu_usage(), pro.memory());
        }

        left_tree.set_headers_visible(true);
        //let filter = gtk::TreeModelFilter::new(&list_store, None);
        scroll.add(&left_tree);
        overlay.add(&scroll);
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let horizontal_layout = gtk::Grid::new();
        /*let list_store1 = list_store.clone();

        filter.set_modify_func(|_, iter, value| {
            list_store1.get_value(&iter, 4).get::<String>()
                                           .expect("get_value from list_store failed")
                       .contains(value.get::<String>().expect("contains.get failed").to_lowercase())
        });*/

        left_tree.connect_cursor_changed(
            clone!(current_pid, kill_button, info_button => move |tree_view| {
                let selection = tree_view.get_selection();
                let (pid, ret) = if let Some((model, iter)) = selection.get_selected() {
                    if let Some(x) = model.get_value(&iter, 0).get::<u32>().map(|x| x as Pid) {
                        (Some(x), true)
                    } else {
                        (None, false)
                    }
                } else {
                    (None, false)
                };
                current_pid.set(pid);
                kill_button.set_sensitive(ret);
                info_button.set_sensitive(ret);
            }
        ));
        kill_button.set_sensitive(false);
        info_button.set_sensitive(false);

        vertical_layout.pack_start(&overlay, true, true, 0);
        horizontal_layout.attach(&info_button, 0, 0, 2, 1);
        horizontal_layout.attach_next_to(&kill_button, Some(&info_button),
                                         gtk::PositionType::Right, 2, 1);
        horizontal_layout.set_column_homogeneous(true);
        vertical_layout.pack_start(&horizontal_layout, false, true, 0);

        // The filter part.
        let filter_model = gtk::TreeModelFilter::new(&list_store, None);
        filter_model.set_visible_func(clone!(filter_entry => move |model, iter| {
            if !filter_entry.get_visible() || filter_entry.get_text_length() < 1 {
                return true;
            }
            if let Some(text) = filter_entry.get_text() {
                if text.is_empty() {
                    return true;
                }
                let text: &str = text.as_ref();
                // TODO: Maybe add an option to make searches case sensitive?
                let pid = model.get_value(iter, 0)
                               .get::<u32>()
                               .map(|p| p.to_string())
                               .unwrap_or_else(String::new);
                let name = model.get_value(iter, 1)
                                .get::<String>()
                                .map(|s| s.to_lowercase())
                                .unwrap_or_else(String::new);
                pid.contains(text) ||
                text.contains(&pid) ||
                name.contains(text) ||
                text.contains(&name)
            } else {
                true
            }
        }));
        // For the filtering to be taken into account, we need to add it directly into the
        // "global" model.
        left_tree.set_model(Some(&filter_model));

        filter_entry.connect_property_text_length_notify(move |_| {
            filter_model.refilter();
        });

        note.create_tab("Process list", &vertical_layout);

        Procs {
            left_tree,
            scroll,
            current_pid,
            kill_button,
            info_button,
            vertical_layout: vertical_layout.downcast::<gtk::Box>().expect("downcast failed"),
            list_store,
            columns,
            filter_entry,
            search_bar,
        }
    }
}

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>, left_tree: &gtk::TreeView,
                 max_width: Option<i32>) {
    let id = v.len() as i32;
    let renderer = gtk::CellRendererText::new();

    if title != "process name" {
        renderer.set_property_xalign(1.0);
    }

    let column = gtk::TreeViewColumn::new();
    column.set_title(title);
    column.set_resizable(true);
    if let Some(max_width) = max_width {
        column.set_max_width(max_width);
        column.set_expand(true);
    }
    column.set_min_width(10);
    column.pack_start(&renderer, true);
    column.add_attribute(&renderer, "text", id);
    column.set_clickable(true);
    column.set_sort_column_id(id);
    left_tree.append_column(&column);
    v.push(column);
}

pub fn create_and_fill_model(list_store: &gtk::ListStore, pid: u32, cmdline: &str, name: &str,
                             cpu: f32, memory: u64) {
    if cmdline.is_empty() || name.is_empty() {
        return;
    }
    list_store.insert_with_values(None,
                                  &[0, 1, 2, 3, 4, 5],
                                  &[&pid,
                                    &name,
                                    &format!("{:.1}", cpu),
                                    &memory,
                                    &name.to_lowercase(),
                                    &cpu
                                   ]);
}
<commit_msg>Fix sorting<commit_after>use glib::object::Cast;
use gtk::{self, Type};
use gtk::{
    BoxExt, CellLayoutExt, CellRendererExt, ContainerExt, EntryExt, GridExt,
    GtkListStoreExtManual, OverlayExt, SearchBarExt, TreeModelExt, TreeModelFilterExt,
    TreeSelectionExt, TreeViewColumnExt, TreeViewExt, WidgetExt,
};

use sysinfo::*;

use notebook::NoteBook;

use std::cell::Cell;
use std::collections::HashMap;
use std::rc::Rc;

#[allow(dead_code)]
pub struct Procs {
    pub left_tree: gtk::TreeView,
    pub scroll: gtk::ScrolledWindow,
    pub current_pid: Rc<Cell<Option<Pid>>>,
    pub kill_button: gtk::Button,
    pub info_button: gtk::Button,
    pub vertical_layout: gtk::Box,
    pub list_store: gtk::ListStore,
    pub columns: Vec<gtk::TreeViewColumn>,
    pub filter_entry: gtk::Entry,
    pub search_bar: gtk::SearchBar,
}

impl Procs {
    pub fn new(proc_list: &HashMap<Pid, Process>, note: &mut NoteBook) -> Procs {
        let left_tree = gtk::TreeView::new();
        let scroll = gtk::ScrolledWindow::new(None::<&gtk::Adjustment>, None::<&gtk::Adjustment>);
        let current_pid = Rc::new(Cell::new(None));
        let kill_button = gtk::Button::new_with_label("End task");
        let info_button = gtk::Button::new_with_label("More information");

        let overlay = gtk::Overlay::new();
        let filter_entry = gtk::Entry::new();
        let search_bar = gtk::SearchBar::new();

        // We put the filter entry at the right bottom.
        filter_entry.set_halign(gtk::Align::End);
        filter_entry.set_valign(gtk::Align::End);
        filter_entry.hide(); // By default, we don't show it.
        search_bar.connect_entry(&filter_entry);
        search_bar.set_show_close_button(true);

        overlay.add_overlay(&filter_entry);

        let mut columns: Vec<gtk::TreeViewColumn> = Vec::new();

        let list_store = gtk::ListStore::new(&[
            // The first four columns of the model are going to be visible in the view.
            Type::U32,       // pid
            Type::String,    // name
            Type::String,    // CPU
            Type::U64,       // mem
            // These two will serve as keys when sorting by process name and CPU usage.
            Type::String,    // name_lowercase
            Type::F32,       // CPU_f32
        ]);

        for pro in proc_list.values() {
            create_and_fill_model(&list_store, pro.pid().as_u32(), &format!("{:?}", &pro.cmd()),
                                  &pro.name(), pro.cpu_usage(), pro.memory());
        }

        left_tree.set_headers_visible(true);
        //let filter = gtk::TreeModelFilter::new(&list_store, None);
        scroll.add(&left_tree);
        overlay.add(&scroll);
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0);
        let horizontal_layout = gtk::Grid::new();
        /*let list_store1 = list_store.clone();

        filter.set_modify_func(|_, iter, value| {
            list_store1.get_value(&iter, 4).get::<String>()
                                           .expect("get_value from list_store failed")
                       .contains(value.get::<String>().expect("contains.get failed").to_lowercase())
        });*/

        left_tree.connect_cursor_changed(
            clone!(current_pid, kill_button, info_button => move |tree_view| {
                let selection = tree_view.get_selection();
                let (pid, ret) = if let Some((model, iter)) = selection.get_selected() {
                    if let Some(x) = model.get_value(&iter, 0).get::<u32>().map(|x| x as Pid) {
                        (Some(x), true)
                    } else {
                        (None, false)
                    }
                } else {
                    (None, false)
                };
                current_pid.set(pid);
                kill_button.set_sensitive(ret);
                info_button.set_sensitive(ret);
            }
        ));
        kill_button.set_sensitive(false);
        info_button.set_sensitive(false);

        vertical_layout.pack_start(&overlay, true, true, 0);
        horizontal_layout.attach(&info_button, 0, 0, 2, 1);
        horizontal_layout.attach_next_to(&kill_button, Some(&info_button),
                                         gtk::PositionType::Right, 2, 1);
        horizontal_layout.set_column_homogeneous(true);
        vertical_layout.pack_start(&horizontal_layout, false, true, 0);

        // The filter part.
        let filter_model = gtk::TreeModelFilter::new(&list_store, None);
        filter_model.set_visible_func(clone!(filter_entry => move |model, iter| {
            if !filter_entry.get_visible() || filter_entry.get_text_length() < 1 {
                return true;
            }
            if let Some(text) = filter_entry.get_text() {
                if text.is_empty() {
                    return true;
                }
                let text: &str = text.as_ref();
                // TODO: Maybe add an option to make searches case sensitive?
                let pid = model.get_value(iter, 0)
                               .get::<u32>()
                               .map(|p| p.to_string())
                               .unwrap_or_else(String::new);
                let name = model.get_value(iter, 1)
                                .get::<String>()
                                .map(|s| s.to_lowercase())
                                .unwrap_or_else(String::new);
                pid.contains(text) ||
                text.contains(&pid) ||
                name.contains(text) ||
                text.contains(&name)
            } else {
                true
            }
        }));
        // For the filtering to be taken into account, we need to add it directly into the
        // "global" model.
        let sort_model = gtk::TreeModelSort::new(&filter_model);
        left_tree.set_model(Some(&sort_model));

        append_column("pid", &mut columns, &left_tree, None);
        append_column("process name", &mut columns, &left_tree, Some(200));
        append_column("cpu usage", &mut columns, &left_tree, None);
        append_column("memory usage (in kB)", &mut columns, &left_tree, None);

        // When we click the "name" column the order is defined by the
        // "name_lowercase" effectively making the built-in comparator ignore case.
        columns[1].set_sort_column_id(4);
        // Likewise clicking the "CPU" column sorts by the "CPU_f32" one because
        // we want the order to be numerical not lexicographical.
        columns[2].set_sort_column_id(5);

        filter_entry.connect_property_text_length_notify(move |_| {
            filter_model.refilter();
        });

        note.create_tab("Process list", &vertical_layout);

        Procs {
            left_tree,
            scroll,
            current_pid,
            kill_button,
            info_button,
            vertical_layout: vertical_layout.downcast::<gtk::Box>().expect("downcast failed"),
            list_store,
            columns,
            filter_entry,
            search_bar,
        }
    }
}

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>, left_tree: &gtk::TreeView,
                 max_width: Option<i32>) {
    let id = v.len() as i32;
    let renderer = gtk::CellRendererText::new();

    if title != "process name" {
        renderer.set_property_xalign(1.0);
    }

    let column = gtk::TreeViewColumn::new();
    column.set_title(title);
    column.set_resizable(true);
    if let Some(max_width) = max_width {
        column.set_max_width(max_width);
        column.set_expand(true);
    }
    column.set_min_width(10);
    column.pack_start(&renderer, true);
    column.add_attribute(&renderer, "text", id);
    column.set_clickable(true);
    column.set_sort_column_id(id);
    left_tree.append_column(&column);
    v.push(column);
}

pub fn create_and_fill_model(list_store: &gtk::ListStore, pid: u32, cmdline: &str, name: &str,
                             cpu: f32, memory: u64) {
    if cmdline.is_empty() || name.is_empty() {
        return;
    }
    list_store.insert_with_values(None,
                                  &[0, 1, 2, 3, 4, 5],
                                  &[&pid,
                                    &name,
                                    &format!("{:.1}", cpu),
                                    &memory,
                                    &name.to_lowercase(),
                                    &cpu
                                   ]);
}
<|endoftext|>