<commit_before>use command::Command;
use vm::VM;
use vm::Cell;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ProgramPatch {
    pub instruction_pointer: usize,
    pub current_depth: u64,
    pub status: ProgramStatus,
}

fn find_by<T, F>(list: &[T], start: Option<usize>, mut predicate: F) -> Option<usize>
where
    F: FnMut(&T) -> bool,
{
    let start = start.unwrap_or(0);
    for (offset, element) in list.iter().enumerate() {
        if predicate(element) {
            return Some(start + offset);
        }
    }

    return None;
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        if let Some(mut index) = self.instruction_pointer {
            while index < self.instructions.len() {
                let current_cell = vm.cells[vm.data_pointer];
                let patch = match (self.instructions[index], self.status) {
                    (_, ProgramStatus::Seeking(goal)) => {
                        Program::forward_jump(
                            &self.instructions,
                            index,
                            goal,
                            self.current_depth,
                        )
                    }

                    (Command::JumpForward, _) => self.process_jump_forward(index, current_cell),

                    (Command::JumpBackward, _) => self.process_jump_backward(index, current_cell),

                    (command, _) => {
                        vm.apply(command);
                        ProgramPatch {
                            status: ProgramStatus::Normal,
                            current_depth: self.current_depth,
                            instruction_pointer: index + 1,
                        }
                    }
                };

                self.update(patch);
                index = patch.instruction_pointer;
            }
        }
    }

    fn process_jump_backward(&self, index: usize, current_cell: Cell) -> ProgramPatch {
        match current_cell {
            0 => ProgramPatch {
                instruction_pointer: index + 1,
                status: ProgramStatus::Normal,
                current_depth: self.current_depth - 1,
            },

            _ => {
                Program::backward_jump(
                    &self.instructions,
                    index,
                    self.current_depth,
                )
            }
        }
    }

    fn process_jump_forward(&self, index: usize, current_cell: Cell) -> ProgramPatch {
        match current_cell {
            0 => {
                Program::forward_jump(
                    &self.instructions,
                    index,
                    self.current_depth,
                    self.current_depth,
                )
            }

            _ => ProgramPatch {
                instruction_pointer: index + 1,
                status: ProgramStatus::Normal,
                current_depth: self.current_depth + 1,
            },
        }
    }

    fn update(&mut self, patch: ProgramPatch) -> &mut Self {
        self.instruction_pointer = Some(patch.instruction_pointer);
        self.current_depth = patch.current_depth;
        self.status = patch.status;
        return self;
    }

    fn backward_jump(commands: &Vec<Command>, start: usize, depth: u64) -> ProgramPatch {
        let goal_depth = depth - 1;
        let mut current_depth = depth;
        for index in (0..start).rev() {
            match commands[index] {
                Command::JumpBackward => current_depth += 1,
                Command::JumpForward => current_depth -= 1,
                _ => {}
            }
            if goal_depth == current_depth {
                return ProgramPatch {
                    instruction_pointer: index + 1,
                    status: ProgramStatus::Normal,
                    current_depth: current_depth + 1,
                };
            }
        }
        panic!("No opening bracket found?");
    }

    fn forward_jump(
        commands: &Vec<Command>,
        index: usize,
        goal_depth: u64,
        mut current_depth: u64,
    ) -> ProgramPatch {
        let index = find_by(&commands[index..], Some(index), |command| {
            match command {
                &Command::JumpBackward => current_depth -= 1,
                &Command::JumpForward => current_depth += 1,
                _ => {}
            };
            return current_depth == goal_depth;
        });

        return match index {
            Some(index) => ProgramPatch {
                instruction_pointer: index + 1,
                status: ProgramStatus::Normal,
                current_depth,
            },

            None => ProgramPatch {
                instruction_pointer: commands.len(),
                status: ProgramStatus::Seeking(goal_depth),
                current_depth,
            },
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn attempt_forward_jump() {
        let commands = vec![
            Command::JumpForward,
            Command::JumpForward,
            Command::JumpBackward,
        ];
        let index = 0;
        let goal_depth = 0;
        let current_depth = 0;
        let actual = Program::attempt_forward_jump(&commands, index, goal_depth, current_depth);
        assert_eq!(
            actual,
            ProgramPatch {
                instruction_pointer: 3,
                current_depth: 1,
                status: ProgramStatus::Seeking(0),
            }
        )
    }
}
<commit_msg>Update unit test names and references<commit_after>use command::Command;
use vm::VM;
use vm::Cell;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ProgramPatch {
    pub instruction_pointer: usize,
    pub current_depth: u64,
    pub status: ProgramStatus,
}

fn find_by<T, F>(list: &[T], start: Option<usize>, mut predicate: F) -> Option<usize>
where
    F: FnMut(&T) -> bool,
{
    let start = start.unwrap_or(0);
    for (offset, element) in list.iter().enumerate() {
        if predicate(element) {
            return Some(start + offset);
        }
    }

    return None;
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        if let Some(mut index) = self.instruction_pointer {
            while index < self.instructions.len() {
                let current_cell = vm.cells[vm.data_pointer];
                let patch = match (self.instructions[index], self.status) {
                    (_, ProgramStatus::Seeking(goal)) => {
                        Program::forward_jump(
                            &self.instructions,
                            index,
                            goal,
                            self.current_depth,
                        )
                    }

                    (Command::JumpForward, _) => self.process_jump_forward(index, current_cell),

                    (Command::JumpBackward, _) => self.process_jump_backward(index, current_cell),

                    (command, _) => {
                        vm.apply(command);
                        ProgramPatch {
                            status: ProgramStatus::Normal,
                            current_depth: self.current_depth,
                            instruction_pointer: index + 1,
                        }
                    }
                };

                self.update(patch);
                index = patch.instruction_pointer;
            }
        }
    }

    fn process_jump_backward(&self, index: usize, current_cell: Cell) -> ProgramPatch {
        match current_cell {
            0 => ProgramPatch {
                instruction_pointer: index + 1,
                status: ProgramStatus::Normal,
                current_depth: self.current_depth - 1,
            },

            _ => {
                Program::backward_jump(
                    &self.instructions,
                    index,
                    self.current_depth,
                )
            }
        }
    }

    fn process_jump_forward(&self, index: usize, current_cell: Cell) -> ProgramPatch {
        match current_cell {
            0 => {
                Program::forward_jump(
                    &self.instructions,
                    index,
                    self.current_depth,
                    self.current_depth,
                )
            }

            _ => ProgramPatch {
                instruction_pointer: index + 1,
                status: ProgramStatus::Normal,
                current_depth: self.current_depth + 1,
            },
        }
    }

    fn update(&mut self, patch: ProgramPatch) -> &mut Self {
        self.instruction_pointer = Some(patch.instruction_pointer);
        self.current_depth = patch.current_depth;
        self.status = patch.status;
        return self;
    }

    fn backward_jump(commands: &Vec<Command>, start: usize, depth: u64) -> ProgramPatch {
        let goal_depth = depth - 1;
        let mut current_depth = depth;
        for index in (0..start).rev() {
            match commands[index] {
                Command::JumpBackward => current_depth += 1,
                Command::JumpForward => current_depth -= 1,
                _ => {}
            }
            if goal_depth == current_depth {
                return ProgramPatch {
                    instruction_pointer: index + 1,
                    status: ProgramStatus::Normal,
                    current_depth: current_depth + 1,
                };
            }
        }
        panic!("No opening bracket found?");
    }

    fn forward_jump(
        commands: &Vec<Command>,
        index: usize,
        goal_depth: u64,
        mut current_depth: u64,
    ) -> ProgramPatch {
        let index = find_by(&commands[index..], Some(index), |command| {
            match command {
                &Command::JumpBackward => current_depth -= 1,
                &Command::JumpForward => current_depth += 1,
                _ => {}
            };
            return current_depth == goal_depth;
        });

        return match index {
            Some(index) => ProgramPatch {
                instruction_pointer: index + 1,
                status: ProgramStatus::Normal,
                current_depth,
            },

            None => ProgramPatch {
                instruction_pointer: commands.len(),
                status: ProgramStatus::Seeking(goal_depth),
                current_depth,
            },
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn forward_jump() {
        let commands = vec![
            Command::JumpForward,
            Command::JumpForward,
            Command::JumpBackward,
        ];
        let index = 0;
        let goal_depth = 0;
        let current_depth = 0;
        let actual = Program::forward_jump(&commands, index, goal_depth, current_depth);
        assert_eq!(
            actual,
            ProgramPatch {
                instruction_pointer: 3,
                current_depth: 1,
                status: ProgramStatus::Seeking(0),
            }
        )
    }
}
<|endoftext|>


<commit_before>use std::cmp::{max, min};

use crate::tokenizer;
use crate::{
    constants::*,
    token_collector::{Style, StyledToken, TokenCollector},
};
use diffus::{
    edit::{self, collection},
    Diffable,
};

/// If more than this percentage of either adds or moves is highlighted, we
/// consider it to be a replacement rather than a move, and skip highlighting
/// it.
const MAX_HIGHLIGHT_PERCENTAGE: usize = 30;

const LARGE_COUNT_CHANGE_PERCENT: usize = 100;
const SMALL_COUNT_CHANGE: usize = 10;

/// Like format!(), but faster for our special case
fn format_simple_line(old_new: &str, plus_minus: char, contents: &str) -> String {
    let mut line = String::with_capacity(old_new.len() + 1 + contents.len() + NORMAL.len());
    line.push_str(old_new);
    line.push(plus_minus);
    line.push_str(contents);
    line.push_str(NORMAL);
    return line;
}

/// Format old and new lines in OLD and NEW colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format(old_text: &str, new_text: &str) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for old_line in old_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        lines.push(format_simple_line(OLD, '-', old_line));
    }
    if (!old_text.is_empty()) && !old_text.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in new_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        lines.push(format_simple_line(NEW, '+', add_line));
    }
    if (!new_text.is_empty()) && !new_text.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

/// Returns the last byte index of the nth line of the given string.
fn last_byte_index_of_nth_line(text: &str, line_count: usize) -> usize {
    let mut newlines_found: usize = 0;
    for (byte_index, c) in text.char_indices() {
        if c != '\n' {
            continue;
        }

        // Newline found
        newlines_found += 1;
        if line_count == newlines_found {
            return byte_index;
        }
    }

    panic!("Line {} not found in \n{}", line_count, text);
}

/// If old has 2 lines and new 30, try highlighting changes between old and the
/// first 2 lines of new.
///
/// Test case: testdata/partial-refine.diff
#[must_use]
fn partial_format(old_text: &str, new_text: &str) -> Vec<String> {
    let old_linecount = old_text.lines().count();
    let new_linecount = new_text.lines().count();

    if old_linecount == new_linecount {
        return simple_format(old_text, new_text);
    }

    if old_linecount > new_linecount {
        // FIXME: Do a partial diff in this case as well rather than just giving
        // up and simple_format()ting the text.
        return simple_format(old_text, new_text);
    }

    // Invariant at this point: old_text has fewer lines than new_text

    if !old_text.ends_with('\n') {
        // old_text does *not* end in a newline

        // FIXME: Write tests for and handle this case, needs some thought on
        // how to poplulate new_initial_lines, and how to merge the results at
        // the end of this function.
        return simple_format(old_text, new_text);
    }

    // Extract the old_linecount initial lines from new_text.
    let new_initial_lines_last_offset = last_byte_index_of_nth_line(new_text, old_linecount);
    let new_remaining_lines_first_offset = new_initial_lines_last_offset + 1;
    let new_initial_lines = &new_text[0..new_remaining_lines_first_offset];

    let mut old_text_vs_new_initial_lines = format(old_text, new_initial_lines);

    // Extract the remaining lines from new_text
    let new_remaining_lines = &new_text[new_remaining_lines_first_offset..];
    let mut new_remaining_lines = simple_format("", new_remaining_lines);

    let mut return_me: Vec<String> = Vec::new();
    return_me.append(&mut old_text_vs_new_initial_lines);
    return_me.append(&mut new_remaining_lines);
    return return_me;
}

/// Returns a vector of ANSI highlighted lines
#[must_use]
pub fn format(old_text: &str, new_text: &str) -> Vec<String> {
    if old_text.is_empty() || new_text.is_empty() {
        return simple_format(old_text, new_text);
    }

    // These checks make us faster, please use the benchmark.py script before
    // and after if you change this.
    if is_large_byte_count_change(old_text, new_text) {
        return partial_format(old_text, new_text);
    }
    if is_large_newline_count_change(old_text, new_text) {
        return partial_format(old_text, new_text);
    }

    // Find diffs between adds and removals
    let mut old_collector = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
    let mut new_collector = TokenCollector::create(StyledToken::new("+".to_string(), Style::New));

    // Tokenize adds and removes before diffing them
    let tokenized_old = tokenizer::tokenize(old_text);
    let tokenized_new = tokenizer::tokenize(new_text);

    let diff = tokenized_old.diff(&tokenized_new);
    match diff {
        edit::Edit::Copy(unchanged) => {
            for token in unchanged {
                old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                new_collector.push(StyledToken::new(token.to_string(), Style::New));
            }
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        collection::Edit::Copy(token) => {
                            old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                            new_collector.push(StyledToken::new(token.to_string(), Style::New));
                        }
                        collection::Edit::Insert(token) => {
                            if *token == "\n" {
                                // Make sure the highlighted linefeed is visible
                                new_collector
                                    .push(StyledToken::new("⏎".to_string(), Style::NewInverse));
                            }
                            new_collector
                                .push(StyledToken::new(token.to_string(), Style::NewInverse));
                        }
                        collection::Edit::Remove(token) => {
                            if *token == "\n" {
                                // Make sure the highlighted linefeed is visible
                                old_collector
                                    .push(StyledToken::new("⏎".to_string(), Style::OldInverse));
                            }
                            old_collector
                                .push(StyledToken::new(token.to_string(), Style::OldInverse));
                        }
                        collection::Edit::Change(_) => unimplemented!("Not implemented, help!"),
                    };
                })
                .for_each(drop);
        }
    }

    let highlighted_old_text = old_collector.render();
    let highlighted_new_text = new_collector.render();

    let highlighted_bytes_count =
        old_collector.highlighted_chars_count() + new_collector.highlighted_chars_count();
    let bytes_count = old_collector.chars_count() + new_collector.chars_count();

    // Don't highlight too much
    if (100 * highlighted_bytes_count) / bytes_count > MAX_HIGHLIGHT_PERCENTAGE {
        return partial_format(old_text, new_text);
    }

    return to_lines(&highlighted_old_text, &highlighted_new_text);
}

#[must_use]
fn is_large_count_change(count1: usize, count2: usize) -> bool {
    // This check makes us ignore some changes, thus making us faster. Please
    // use the benchmark.py script before and after if you touch this code.

    let high_count = max(count1, count2);
    let low_count = min(count1, count2);

    if high_count - low_count <= SMALL_COUNT_CHANGE {
        return false;
    }

    // "+ 99" makes the result round up, so 0->0, 1->2.
    let low_count_plus_percentage = (low_count * (LARGE_COUNT_CHANGE_PERCENT + 100) + 99) / 100;

    return high_count >= low_count_plus_percentage;
}

#[must_use]
fn is_large_byte_count_change(old_text: &str, new_text: &str) -> bool {
    return is_large_count_change(old_text.len(), new_text.len());
}

#[must_use]
fn is_large_newline_count_change(old_text: &str, new_text: &str) -> bool {
    let old_newline_count = bytecount::count(old_text.as_bytes(), b'\n');
    let new_newline_count = bytecount::count(new_text.as_bytes(), b'\n');
    return is_large_count_change(old_newline_count, new_newline_count);
}

#[must_use]
fn to_lines(old: &str, new: &str) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();
    for highlighted_old_line in old.lines() {
        lines.push(highlighted_old_line.to_string());
    }
    if (!old.is_empty()) && !old.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for highlighted_new_line in new.lines() {
        lines.push(highlighted_new_line.to_string());
    }
    if (!new.is_empty()) && !new.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(simple_format(&"".to_string(), &"".to_string()), empty);

        // Test adds-only
        assert_eq!(
            simple_format(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + NEW + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + NEW + "+a" + NORMAL,
                "".to_string() + NEW + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + OLD + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + OLD + "-a" + NORMAL,
                "".to_string() + OLD + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        let result = format(&"<quotes>\n".to_string(), &"[quotes]\n".to_string());
        assert_eq!(
            result,
            [
                format!(
                    "{}-{}<{}quotes{}>{}",
                    OLD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}",
                    NEW, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    #[test]
    fn test_almost_empty_changes() {
        let result = format(&"x\n".to_string(), &"".to_string());
        assert_eq!(result, [format!("{}-x{}", OLD, NORMAL),]);

        let result = format(&"".to_string(), &"x\n".to_string());
        assert_eq!(result, [format!("{}+x{}", NEW, NORMAL),]);
    }

    #[test]
    fn test_is_large_byte_count_change() {
        assert_eq!(is_large_byte_count_change("", ""), false);

        assert_eq!(
            is_large_byte_count_change("", &"x".repeat(SMALL_COUNT_CHANGE)),
            false
        );
        assert_eq!(
            is_large_byte_count_change("", &"x".repeat(SMALL_COUNT_CHANGE + 1)),
            true
        );

        // Verify that doubling the length counts as large
        let base_len = SMALL_COUNT_CHANGE * 2;
        let double_len = base_len * 2;
        let almost_double_len = double_len - 1;
        assert_eq!(
            is_large_byte_count_change(&"x".repeat(base_len), &"y".repeat(almost_double_len)),
            false
        );
        assert_eq!(
            is_large_byte_count_change(&"x".repeat(base_len), &"y".repeat(double_len)),
            true
        );
    }
}
<commit_msg>Initial attempt<commit_after>use std::cmp::{max, min};

use crate::tokenizer;
use crate::{
    constants::*,
    token_collector::{Style, StyledToken, TokenCollector},
};
use diffus::{
    edit::{self, collection},
    Diffable,
};

/// If more than this percentage of either adds or moves is highlighted, we
/// consider it to be a replacement rather than a move, and skip highlighting
/// it.
const MAX_HIGHLIGHT_PERCENTAGE: usize = 30;

const LARGE_COUNT_CHANGE_PERCENT: usize = 100;
const SMALL_COUNT_CHANGE: usize = 10;

/// Like format!(), but faster for our special case
fn format_simple_line(old_new: &str, plus_minus: char, contents: &str) -> String {
    let mut line = String::with_capacity(old_new.len() + 1 + contents.len() + NORMAL.len());
    line.push_str(old_new);
    line.push(plus_minus);
    line.push_str(contents);
    line.push_str(NORMAL);
    return line;
}

/// Format old and new lines in OLD and NEW colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format(old_text: &str, new_text: &str) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for old_line in old_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        lines.push(format_simple_line(OLD, '-', old_line));
    }
    if (!old_text.is_empty()) && !old_text.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in new_text.lines() {
        // Use a specialized line formatter since this code is in a hot path
        lines.push(format_simple_line(NEW, '+', add_line));
    }
    if (!new_text.is_empty()) && !new_text.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

/// Returns the last byte index of the nth line of the given string.
fn last_byte_index_of_nth_line(text: &str, line_count: usize) -> usize {
    let mut newlines_found: usize = 0;
    for (byte_index, c) in text.char_indices() {
        if c != '\n' {
            continue;
        }

        // Newline found
        newlines_found += 1;
        if line_count == newlines_found {
            return byte_index;
        }
    }

    panic!("Line {} not found in \n{}", line_count, text);
}

/// If old has 2 lines and new 30, try highlighting changes between old and the
/// first 2 lines of new.
///
/// Test case: testdata/partial-refine.diff
#[must_use]
fn partial_format(old_text: &str, new_text: &str) -> Vec<String> {
    let old_linecount = old_text.lines().count();
    let new_linecount = new_text.lines().count();

    if old_linecount == new_linecount {
        return simple_format(old_text, new_text);
    }

    if old_linecount > new_linecount {
        return partial_format_shortened(old_text, new_text);
    }

    // Invariant at this point: old_text has fewer lines than new_text

    if !old_text.ends_with('\n') {
        // old_text does *not* end in a newline

        // FIXME: Write tests for and handle this case, needs some thought on
        // how to poplulate new_initial_lines, and how to merge the results at
        // the end of this function.
        return simple_format(old_text, new_text);
    }

    // Extract the old_linecount initial lines from new_text.
    let new_initial_lines_last_offset = last_byte_index_of_nth_line(new_text, old_linecount);
    let new_remaining_lines_first_offset = new_initial_lines_last_offset + 1;
    let new_initial_lines = &new_text[0..new_remaining_lines_first_offset];

    let mut old_text_vs_new_initial_lines = format(old_text, new_initial_lines);

    // Extract the remaining lines from new_text
    let new_remaining_lines = &new_text[new_remaining_lines_first_offset..];
    let mut new_remaining_lines = simple_format("", new_remaining_lines);

    let mut return_me: Vec<String> = Vec::new();
    return_me.append(&mut old_text_vs_new_initial_lines);
    return_me.append(&mut new_remaining_lines);
    return return_me;
}

/// If old has 30 lines and new 2, try highlighting changes between the first 2
/// lines of old and new.
///
/// Test case: testdata/shorten-section.diff
///
/// See also partial_format() which is the opposite of this function.
#[must_use]
fn partial_format_shortened(old_text: &str, new_text: &str) -> Vec<String> {
    // Invariant at this point: old_text has more lines than new_text

    if !new_text.ends_with('\n') {
        // new_text does *not* end in a newline

        // FIXME: Write tests for and handle this case, needs some thought on
        // how to poplulate old_initial_lines, and how to merge the results at
        // the end of this function.
        return simple_format(old_text, new_text);
    }

    // Extract the new_linecount initial lines from old_text.
    let new_linecount = new_text.lines().count();
    let old_initial_lines_last_offset = last_byte_index_of_nth_line(old_text, new_linecount);
    let old_remaining_lines_first_offset = old_initial_lines_last_offset + 1;
    let old_initial_lines = &old_text[0..old_remaining_lines_first_offset];

    let mut new_text_vs_old_initial_lines = format(old_initial_lines, new_text);

    // Extract the remaining lines from new_text
    let old_remaining_lines = &old_text[old_remaining_lines_first_offset..];
    let mut old_remaining_lines = simple_format(old_remaining_lines, "");

    let mut return_me: Vec<String> = Vec::new();

    // FIXME: These lines need to be appended *between* the old and new lines
    // returned by new_text_vs_old_initial_lines
    return_me.append(&mut old_remaining_lines);

    return_me.append(&mut new_text_vs_old_initial_lines);
    return return_me;
}

/// Returns a vector of ANSI highlighted lines
#[must_use]
pub fn format(old_text: &str, new_text: &str) -> Vec<String> {
    if old_text.is_empty() || new_text.is_empty() {
        return simple_format(old_text, new_text);
    }

    // These checks make us faster, please use the benchmark.py script before
    // and after if you change this.
    if is_large_byte_count_change(old_text, new_text) {
        return partial_format(old_text, new_text);
    }
    if is_large_newline_count_change(old_text, new_text) {
        return partial_format(old_text, new_text);
    }

    // Find diffs between adds and removals
    let mut old_collector = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
    let mut new_collector = TokenCollector::create(StyledToken::new("+".to_string(), Style::New));

    // Tokenize adds and removes before diffing them
    let tokenized_old = tokenizer::tokenize(old_text);
    let tokenized_new = tokenizer::tokenize(new_text);

    let diff = tokenized_old.diff(&tokenized_new);
    match diff {
        edit::Edit::Copy(unchanged) => {
            for token in unchanged {
                old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                new_collector.push(StyledToken::new(token.to_string(), Style::New));
            }
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        collection::Edit::Copy(token) => {
                            old_collector.push(StyledToken::new(token.to_string(), Style::Old));
                            new_collector.push(StyledToken::new(token.to_string(), Style::New));
                        }
                        collection::Edit::Insert(token) => {
                            if *token == "\n" {
                                // Make sure the highlighted linefeed is visible
                                new_collector
                                    .push(StyledToken::new("⏎".to_string(), Style::NewInverse));
                            }
                            new_collector
                                .push(StyledToken::new(token.to_string(), Style::NewInverse));
                        }
                        collection::Edit::Remove(token) => {
                            if *token == "\n" {
                                // Make sure the highlighted linefeed is visible
                                old_collector
                                    .push(StyledToken::new("⏎".to_string(), Style::OldInverse));
                            }
                            old_collector
                                .push(StyledToken::new(token.to_string(), Style::OldInverse));
                        }
                        collection::Edit::Change(_) => unimplemented!("Not implemented, help!"),
                    };
                })
                .for_each(drop);
        }
    }

    let highlighted_old_text = old_collector.render();
    let highlighted_new_text = new_collector.render();

    let highlighted_bytes_count =
        old_collector.highlighted_chars_count() + new_collector.highlighted_chars_count();
    let bytes_count = old_collector.chars_count() + new_collector.chars_count();

    // Don't highlight too much
    if (100 * highlighted_bytes_count) / bytes_count > MAX_HIGHLIGHT_PERCENTAGE {
        return partial_format(old_text, new_text);
    }

    return to_lines(&highlighted_old_text, &highlighted_new_text);
}

#[must_use]
fn is_large_count_change(count1: usize, count2: usize) -> bool {
    // This check makes us ignore some changes, thus making us faster. Please
    // use the benchmark.py script before and after if you touch this code.

    let high_count = max(count1, count2);
    let low_count = min(count1, count2);

    if high_count - low_count <= SMALL_COUNT_CHANGE {
        return false;
    }

    // "+ 99" makes the result round up, so 0->0, 1->2.
    let low_count_plus_percentage = (low_count * (LARGE_COUNT_CHANGE_PERCENT + 100) + 99) / 100;

    return high_count >= low_count_plus_percentage;
}

#[must_use]
fn is_large_byte_count_change(old_text: &str, new_text: &str) -> bool {
    return is_large_count_change(old_text.len(), new_text.len());
}

#[must_use]
fn is_large_newline_count_change(old_text: &str, new_text: &str) -> bool {
    let old_newline_count = bytecount::count(old_text.as_bytes(), b'\n');
    let new_newline_count = bytecount::count(new_text.as_bytes(), b'\n');
    return is_large_count_change(old_newline_count, new_newline_count);
}

#[must_use]
fn to_lines(old: &str, new: &str) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();
    for highlighted_old_line in old.lines() {
        lines.push(highlighted_old_line.to_string());
    }
    if (!old.is_empty()) && !old.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for highlighted_new_line in new.lines() {
        lines.push(highlighted_new_line.to_string());
    }
    if (!new.is_empty()) && !new.ends_with('\n') {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(simple_format(&"".to_string(), &"".to_string()), empty);

        // Test adds-only
        assert_eq!(
            simple_format(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + NEW + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + NEW + "+a" + NORMAL,
                "".to_string() + NEW + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + OLD + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + OLD + "-a" + NORMAL,
                "".to_string() + OLD + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        let result = format(&"<quotes>\n".to_string(), &"[quotes]\n".to_string());
        assert_eq!(
            result,
            [
                format!(
                    "{}-{}<{}quotes{}>{}",
                    OLD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}",
                    NEW, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    #[test]
    fn test_almost_empty_changes() {
        let result = format(&"x\n".to_string(), &"".to_string());
        assert_eq!(result, [format!("{}-x{}", OLD, NORMAL),]);

        let result = format(&"".to_string(), &"x\n".to_string());
        assert_eq!(result, [format!("{}+x{}", NEW, NORMAL),]);
    }

    #[test]
    fn test_is_large_byte_count_change() {
        assert_eq!(is_large_byte_count_change("", ""), false);

        assert_eq!(
            is_large_byte_count_change("", &"x".repeat(SMALL_COUNT_CHANGE)),
            false
        );
        assert_eq!(
            is_large_byte_count_change("", &"x".repeat(SMALL_COUNT_CHANGE + 1)),
            true
        );

        // Verify that doubling the length counts as large
        let base_len = SMALL_COUNT_CHANGE * 2;
        let double_len = base_len * 2;
        let almost_double_len = double_len - 1;
        assert_eq!(
            is_large_byte_count_change(&"x".repeat(base_len), &"y".repeat(almost_double_len)),
            false
        );
        assert_eq!(
            is_large_byte_count_change(&"x".repeat(base_len), &"y".repeat(double_len)),
            true
        );
    }
}
<|endoftext|>


<commit_before>use std::cell::{Ref, RefMut};
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;

use actix_http::http::{HeaderMap, Method, Uri, Version};
use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead};
use actix_router::{Path, Url};

use crate::config::AppConfig;
use crate::error::UrlGenerationError;
use crate::extract::FromRequest;
use crate::info::ConnectionInfo;
use crate::rmap::ResourceMap;
use crate::service::ServiceFromRequest;

#[derive(Clone)]
/// An HTTP Request
pub struct HttpRequest {
    pub(crate) head: Message<RequestHead>,
    pub(crate) path: Path<Url>,
    rmap: Rc<ResourceMap>,
    config: AppConfig,
}

impl HttpRequest {
    #[inline]
    pub(crate) fn new(
        head: Message<RequestHead>,
        path: Path<Url>,
        rmap: Rc<ResourceMap>,
        config: AppConfig,
    ) -> HttpRequest {
        HttpRequest {
            head,
            path,
            rmap,
            config,
        }
    }
}

impl HttpRequest {
    /// This method returns reference to the request head
    #[inline]
    pub fn head(&self) -> &RequestHead {
        &self.head
    }

    /// Request's uri.
    #[inline]
    pub fn uri(&self) -> &Uri {
        &self.head().uri
    }

    /// Read the Request method.
    #[inline]
    pub fn method(&self) -> &Method {
        &self.head().method
    }

    /// Read the Request Version.
    #[inline]
    pub fn version(&self) -> Version {
        self.head().version
    }

    /// The target path of this Request.
    #[inline]
    pub fn path(&self) -> &str {
        self.head().uri.path()
    }

    /// The query string in the URL.
    ///
    /// E.g., id=10
    #[inline]
    pub fn query_string(&self) -> &str {
        if let Some(query) = self.uri().query().as_ref() {
            query
        } else {
            ""
        }
    }

    /// Get a reference to the Path parameters.
    ///
    /// Params is a container for url parameters.
    /// A variable segment is specified in the form `{identifier}`,
    /// where the identifier can be used later in a request handler to
    /// access the matched value for that segment.
    #[inline]
    pub fn match_info(&self) -> &Path<Url> {
        &self.path
    }

    /// App config
    #[inline]
    pub fn config(&self) -> &AppConfig {
        &self.config
    }

    /// Generate url for named resource
    ///
    /// ```rust
    /// # extern crate actix_web;
    /// # use actix_web::{web, App, HttpRequest, HttpResponse};
    /// #
    /// fn index(req: HttpRequest) -> HttpResponse {
    ///     let url = req.url_for("foo", &["1", "2", "3"]); // <- generate url for "foo" resource
    ///     HttpResponse::Ok().into()
    /// }
    ///
    /// fn main() {
    ///     let app = App::new()
    ///         .service(web::resource("/test/{one}/{two}/{three}")
    ///              .name("foo")  // <- set resource name, then it could be used in `url_for`
    ///              .route(web::get().to(|| HttpResponse::Ok()))
    ///         );
    /// }
    /// ```
    pub fn url_for<U, I>(
        &self,
        name: &str,
        elements: U,
    ) -> Result<url::Url, UrlGenerationError>
    where
        U: IntoIterator<Item = I>,
        I: AsRef<str>,
    {
        self.rmap.url_for(&self, name, elements)
    }

    /// Generate url for named resource
    ///
    /// This method is similar to `HttpRequest::url_for()` but it can be used
    /// for urls that do not contain variable parts.
    pub fn url_for_static(&self, name: &str) -> Result<url::Url, UrlGenerationError> {
        const NO_PARAMS: [&str; 0] = [];
        self.url_for(name, &NO_PARAMS)
    }

    /// Get *ConnectionInfo* for the current request.
    #[inline]
    pub fn connection_info(&self) -> Ref<ConnectionInfo> {
        ConnectionInfo::get(&*self, &*self.config())
    }
}

impl Deref for HttpRequest {
    type Target = RequestHead;

    fn deref(&self) -> &RequestHead {
        self.head()
    }
}

impl HttpMessage for HttpRequest {
    type Stream = ();

    #[inline]
    /// Returns Request's headers.
    fn headers(&self) -> &HeaderMap {
        &self.head().headers
    }

    /// Request extensions
    #[inline]
    fn extensions(&self) -> Ref<Extensions> {
        self.head.extensions()
    }

    /// Mutable reference to a the request's extensions
    #[inline]
    fn extensions_mut(&self) -> RefMut<Extensions> {
        self.head.extensions_mut()
    }

    #[inline]
    fn take_payload(&mut self) -> Payload<Self::Stream> {
        Payload::None
    }
}

/// It is possible to get `HttpRequest` as an extractor handler parameter
///
/// ## Example
///
/// ```rust
/// # #[macro_use] extern crate serde_derive;
/// use actix_web::{web, App, HttpRequest};
///
/// /// extract `Thing` from request
/// fn index(req: HttpRequest) -> String {
///    format!("Got thing: {:?}", req)
/// }
///
/// fn main() {
///     let app = App::new().service(
///         web::resource("/users/{first}").route(
///             web::get().to(index))
///     );
/// }
/// ```
impl<P> FromRequest<P> for HttpRequest {
    type Error = Error;
    type Future = Result<Self, Error>;

    #[inline]
    fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
        Ok(req.clone())
    }
}

impl fmt::Debug for HttpRequest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(
            f,
            "\nHttpRequest {:?} {}:{}",
            self.head.version,
            self.head.method,
            self.path()
        )?;
        if !self.query_string().is_empty() {
            writeln!(f, "  query: ?{:?}", self.query_string())?;
        }
        if !self.match_info().is_empty() {
            writeln!(f, "  params: {:?}", self.match_info())?;
        }
        writeln!(f, "  headers:")?;
        for (key, val) in self.headers().iter() {
            writeln!(f, "    {:?}: {:?}", key, val)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::dev::{ResourceDef, ResourceMap};
    use crate::http::header;
    use crate::test::TestRequest;

    #[test]
    fn test_debug() {
        let req =
            TestRequest::with_header("content-type", "text/plain").to_http_request();
        let dbg = format!("{:?}", req);
        assert!(dbg.contains("HttpRequest"));
    }

    #[test]
    #[cfg(feature = "cookies")]
    fn test_no_request_cookies() {
        let req = TestRequest::default().to_http_request();
        assert!(req.cookies().unwrap().is_empty());
    }

    #[test]
    #[cfg(feature = "cookies")]
    fn test_request_cookies() {
        let req = TestRequest::default()
            .header(header::COOKIE, "cookie1=value1")
            .header(header::COOKIE, "cookie2=value2")
            .to_http_request();
        {
            let cookies = req.cookies().unwrap();
            assert_eq!(cookies.len(), 2);
            assert_eq!(cookies[0].name(), "cookie1");
            assert_eq!(cookies[0].value(), "value1");
            assert_eq!(cookies[1].name(), "cookie2");
            assert_eq!(cookies[1].value(), "value2");
        }

        let cookie = req.cookie("cookie1");
        assert!(cookie.is_some());
        let cookie = cookie.unwrap();
        assert_eq!(cookie.name(), "cookie1");
        assert_eq!(cookie.value(), "value1");

        let cookie = req.cookie("cookie-unknown");
        assert!(cookie.is_none());
    }

    #[test]
    fn test_request_query() {
        let req = TestRequest::with_uri("/?id=test").to_http_request();
        assert_eq!(req.query_string(), "id=test");
    }

    #[test]
    fn test_url_for() {
        let mut res = ResourceDef::new("/user/{name}.{ext}");
        *res.name_mut() = "index".to_string();

        let mut rmap = ResourceMap::new(ResourceDef::new(""));
        rmap.add(&mut res, None);
        assert!(rmap.has_resource("/user/test.html"));
        assert!(!rmap.has_resource("/test/unknown"));

        let req = TestRequest::with_header(header::HOST, "www.rust-lang.org")
            .rmap(rmap)
            .to_http_request();

        assert_eq!(
            req.url_for("unknown", &["test"]),
            Err(UrlGenerationError::ResourceNotFound)
        );
        assert_eq!(
            req.url_for("index", &["test"]),
            Err(UrlGenerationError::NotEnoughElements)
        );
        let url = req.url_for("index", &["test", "html"]);
        assert_eq!(
            url.ok().unwrap().as_str(),
            "http://www.rust-lang.org/user/test.html"
        );
    }

    #[test]
    fn test_url_for_static() {
        let mut rdef = ResourceDef::new("/index.html");
        *rdef.name_mut() = "index".to_string();

        let mut rmap = ResourceMap::new(ResourceDef::new(""));
        rmap.add(&mut rdef, None);

        assert!(rmap.has_resource("/index.html"));

        let req = TestRequest::with_uri("/test")
            .header(header::HOST, "www.rust-lang.org")
            .rmap(rmap)
            .to_http_request();
        let url = req.url_for_static("index");
        assert_eq!(
            url.ok().unwrap().as_str(),
            "http://www.rust-lang.org/index.html"
        );
    }

    #[test]
    fn test_url_for_external() {
        let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");

        *rdef.name_mut() = "youtube".to_string();

        let mut rmap = ResourceMap::new(ResourceDef::new(""));
        rmap.add(&mut rdef, None);
        assert!(rmap.has_resource("https://youtube.com/watch/unknown"));

        let req = TestRequest::default().rmap(rmap).to_http_request();
        let url = req.url_for("youtube", &["oHg5SJYRHA0"]);
        assert_eq!(
            url.ok().unwrap().as_str(),
            "https://youtube.com/watch/oHg5SJYRHA0"
        );
    }
}
<commit_msg>allow to get app data via HttpRequest<commit_after>use std::cell::{Ref, RefMut};
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;

use actix_http::http::{HeaderMap, Method, Uri, Version};
use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead};
use actix_router::{Path, Url};

use crate::config::AppConfig;
use crate::data::Data;
use crate::error::UrlGenerationError;
use crate::extract::FromRequest;
use crate::info::ConnectionInfo;
use crate::rmap::ResourceMap;
use crate::service::ServiceFromRequest;

#[derive(Clone)]
/// An HTTP Request
pub struct HttpRequest {
    pub(crate) head: Message<RequestHead>,
    pub(crate) path: Path<Url>,
    rmap: Rc<ResourceMap>,
    config: AppConfig,
}

impl HttpRequest {
    #[inline]
    pub(crate) fn new(
        head: Message<RequestHead>,
        path: Path<Url>,
        rmap: Rc<ResourceMap>,
        config: AppConfig,
    ) -> HttpRequest {
        HttpRequest {
            head,
            path,
            rmap,
            config,
        }
    }
}

impl HttpRequest {
    /// This method returns reference to the request head
    #[inline]
    pub fn head(&self) -> &RequestHead {
        &self.head
    }

    /// Request's uri.
    #[inline]
    pub fn uri(&self) -> &Uri {
        &self.head().uri
    }

    /// Read the Request method.
    #[inline]
    pub fn method(&self) -> &Method {
        &self.head().method
    }

    /// Read the Request Version.
    #[inline]
    pub fn version(&self) -> Version {
        self.head().version
    }

    /// The target path of this Request.
    #[inline]
    pub fn path(&self) -> &str {
        self.head().uri.path()
    }

    /// The query string in the URL.
    ///
    /// E.g., id=10
    #[inline]
    pub fn query_string(&self) -> &str {
        if let Some(query) = self.uri().query().as_ref() {
            query
        } else {
            ""
        }
    }

    /// Get a reference to the Path parameters.
    ///
    /// Params is a container for url parameters.
    /// A variable segment is specified in the form `{identifier}`,
    /// where the identifier can be used later in a request handler to
    /// access the matched value for that segment.
    #[inline]
    pub fn match_info(&self) -> &Path<Url> {
        &self.path
    }

    /// App config
    #[inline]
    pub fn config(&self) -> &AppConfig {
        &self.config
    }

    /// Get an application data stored with `App::data()` method during
    /// application configuration.
    pub fn app_data<T: 'static>(&self) -> Option<Data<T>> {
        if let Some(st) = self.config.extensions().get::<Data<T>>() {
            Some(st.clone())
        } else {
            None
        }
    }

    /// Generate url for named resource
    ///
    /// ```rust
    /// # extern crate actix_web;
    /// # use actix_web::{web, App, HttpRequest, HttpResponse};
    /// #
    /// fn index(req: HttpRequest) -> HttpResponse {
    ///     let url = req.url_for("foo", &["1", "2", "3"]); // <- generate url for "foo" resource
    ///     HttpResponse::Ok().into()
    /// }
    ///
    /// fn main() {
    ///     let app = App::new()
    ///         .service(web::resource("/test/{one}/{two}/{three}")
    ///              .name("foo")  // <- set resource name, then it could be used in `url_for`
    ///              .route(web::get().to(|| HttpResponse::Ok()))
    ///         );
    /// }
    /// ```
    pub fn url_for<U, I>(
        &self,
        name: &str,
        elements: U,
    ) -> Result<url::Url, UrlGenerationError>
    where
        U: IntoIterator<Item = I>,
        I: AsRef<str>,
    {
        self.rmap.url_for(&self, name, elements)
    }

    /// Generate url for named resource
    ///
    /// This method is similar to `HttpRequest::url_for()` but it can be used
    /// for urls that do not contain variable parts.
    pub fn url_for_static(&self, name: &str) -> Result<url::Url, UrlGenerationError> {
        const NO_PARAMS: [&str; 0] = [];
        self.url_for(name, &NO_PARAMS)
    }

    /// Get *ConnectionInfo* for the current request.
    #[inline]
    pub fn connection_info(&self) -> Ref<ConnectionInfo> {
        ConnectionInfo::get(&*self, &*self.config())
    }
}

impl Deref for HttpRequest {
    type Target = RequestHead;

    fn deref(&self) -> &RequestHead {
        self.head()
    }
}

impl HttpMessage for HttpRequest {
    type Stream = ();

    #[inline]
    /// Returns Request's headers.
    fn headers(&self) -> &HeaderMap {
        &self.head().headers
    }

    /// Request extensions
    #[inline]
    fn extensions(&self) -> Ref<Extensions> {
        self.head.extensions()
    }

    /// Mutable reference to a the request's extensions
    #[inline]
    fn extensions_mut(&self) -> RefMut<Extensions> {
        self.head.extensions_mut()
    }

    #[inline]
    fn take_payload(&mut self) -> Payload<Self::Stream> {
        Payload::None
    }
}

/// It is possible to get `HttpRequest` as an extractor handler parameter
///
/// ## Example
///
/// ```rust
/// # #[macro_use] extern crate serde_derive;
/// use actix_web::{web, App, HttpRequest};
///
/// /// extract `Thing` from request
/// fn index(req: HttpRequest) -> String {
///    format!("Got thing: {:?}", req)
/// }
///
/// fn main() {
///     let app = App::new().service(
///         web::resource("/users/{first}").route(
///             web::get().to(index))
///     );
/// }
/// ```
impl<P> FromRequest<P> for HttpRequest {
    type Error = Error;
    type Future = Result<Self, Error>;

    #[inline]
    fn from_request(req: &mut ServiceFromRequest<P>) -> Self::Future {
        Ok(req.clone())
    }
}

impl fmt::Debug for HttpRequest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(
            f,
            "\nHttpRequest {:?} {}:{}",
            self.head.version,
            self.head.method,
            self.path()
        )?;
        if !self.query_string().is_empty() {
            writeln!(f, "  query: ?{:?}", self.query_string())?;
        }
        if !self.match_info().is_empty() {
            writeln!(f, "  params: {:?}", self.match_info())?;
        }
        writeln!(f, "  headers:")?;
        for (key, val) in self.headers().iter() {
            writeln!(f, "    {:?}: {:?}", key, val)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::dev::{ResourceDef, ResourceMap};
    use crate::http::{header, StatusCode};
    use crate::test::{call_success, init_service, TestRequest};
    use crate::{web, App, HttpResponse};

    #[test]
    fn test_debug() {
        let req =
            TestRequest::with_header("content-type", "text/plain").to_http_request();
        let dbg = format!("{:?}", req);
        assert!(dbg.contains("HttpRequest"));
    }

    #[test]
    #[cfg(feature = "cookies")]
    fn test_no_request_cookies() {
        let req = TestRequest::default().to_http_request();
        assert!(req.cookies().unwrap().is_empty());
    }

    #[test]
    #[cfg(feature = "cookies")]
    fn test_request_cookies() {
        let req = TestRequest::default()
            .header(header::COOKIE, "cookie1=value1")
            .header(header::COOKIE, "cookie2=value2")
            .to_http_request();
        {
            let cookies = req.cookies().unwrap();
            assert_eq!(cookies.len(), 2);
            assert_eq!(cookies[0].name(), "cookie1");
            assert_eq!(cookies[0].value(), "value1");
            assert_eq!(cookies[1].name(), "cookie2");
            assert_eq!(cookies[1].value(), "value2");
        }

        let cookie = req.cookie("cookie1");
        assert!(cookie.is_some());
        let cookie = cookie.unwrap();
        assert_eq!(cookie.name(), "cookie1");
        assert_eq!(cookie.value(), "value1");

        let cookie = req.cookie("cookie-unknown");
        assert!(cookie.is_none());
    }

    #[test]
    fn test_request_query() {
        let req = TestRequest::with_uri("/?id=test").to_http_request();
        assert_eq!(req.query_string(), "id=test");
    }

    #[test]
    fn test_url_for() {
        let mut res = ResourceDef::new("/user/{name}.{ext}");
        *res.name_mut() = "index".to_string();

        let mut rmap = ResourceMap::new(ResourceDef::new(""));
        rmap.add(&mut res, None);
        assert!(rmap.has_resource("/user/test.html"));
        assert!(!rmap.has_resource("/test/unknown"));

        let req = TestRequest::with_header(header::HOST, "www.rust-lang.org")
            .rmap(rmap)
            .to_http_request();

        assert_eq!(
            req.url_for("unknown", &["test"]),
            Err(UrlGenerationError::ResourceNotFound)
        );
        assert_eq!(
            req.url_for("index", &["test"]),
            Err(UrlGenerationError::NotEnoughElements)
        );
        let url = req.url_for("index", &["test", "html"]);
        assert_eq!(
            url.ok().unwrap().as_str(),
            "http://www.rust-lang.org/user/test.html"
        );
    }

    #[test]
    fn test_url_for_static() {
        let mut rdef = ResourceDef::new("/index.html");
        *rdef.name_mut() = "index".to_string();

        let mut rmap = ResourceMap::new(ResourceDef::new(""));
        rmap.add(&mut rdef, None);

        assert!(rmap.has_resource("/index.html"));

        let req = TestRequest::with_uri("/test")
            .header(header::HOST, "www.rust-lang.org")
            .rmap(rmap)
            .to_http_request();
        let url = req.url_for_static("index");
        assert_eq!(
            url.ok().unwrap().as_str(),
            "http://www.rust-lang.org/index.html"
        );
    }

    #[test]
    fn test_url_for_external() {
        let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");

        *rdef.name_mut() = "youtube".to_string();

        let mut rmap = ResourceMap::new(ResourceDef::new(""));
        rmap.add(&mut rdef, None);
        assert!(rmap.has_resource("https://youtube.com/watch/unknown"));

        let req = TestRequest::default().rmap(rmap).to_http_request();
        let url = req.url_for("youtube", &["oHg5SJYRHA0"]);
        assert_eq!(
            url.ok().unwrap().as_str(),
            "https://youtube.com/watch/oHg5SJYRHA0"
        );
    }

    #[test]
    fn test_app_data() {
        let mut srv = init_service(App::new().data(10usize).service(
            web::resource("/").to(|req: HttpRequest| {
                if req.app_data::<usize>().is_some() {
                    HttpResponse::Ok()
                } else {
                    HttpResponse::BadRequest()
                }
            }),
        ));

        let req = TestRequest::default().to_request();
        let resp = call_success(&mut srv, req);
        assert_eq!(resp.status(), StatusCode::OK);

        let mut srv = init_service(App::new().data(10u32).service(
            web::resource("/").to(|req: HttpRequest| {
                if req.app_data::<usize>().is_some() {
                    HttpResponse::Ok()
                } else {
                    HttpResponse::BadRequest()
                }
            }),
        ));

        let req = TestRequest::default().to_request();
        let resp = call_success(&mut srv, req);
        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::error::Error;

#[derive(Debug)]
pub struct ParsingError {
    desc: String,
}

impl fmt::Display for ParsingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(f)
    }
}

impl Error for ParsingError {
    fn description(&self) -> &str {
        self.desc.as_slice()
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl ParsingError {
    pub fn new(desc: String) -> Self {
        ParsingError {
            desc: desc
        }
    }
}

pub type ParsingResult<T> = Result<T, ParsingError>;
<commit_msg>Add fixme for parsing error<commit_after>use std::fmt;
use std::error::Error;

#[derive(Debug)]
pub struct ParsingError {
    desc: String,  // FIXME: Too basic
}

impl fmt::Display for ParsingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(f)
    }
}

impl Error for ParsingError {
    fn description(&self) -> &str {
        self.desc.as_slice()
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl ParsingError {
    pub fn new(desc: String) -> Self {
        ParsingError {
            desc: desc
        }
    }
}

pub type ParsingResult<T> = Result<T, ParsingError>;
<|endoftext|>


<commit_before>use error::Error;
use pos::Pos;
use token::{Token, TokenType};

pub struct Scanner {
    data: String,
    index: usize,
    start_pos: Pos,
    curr_pos: Pos,
}

impl Scanner {
    // Create a new scanner from a given program represented as a
    // String.
    pub fn new(data: String) -> Self {
        Scanner {
            data: data,
            index: 0,
            start_pos: Pos { line: 1, col: 1 },
            curr_pos: Pos { line: 1, col: 1 },
        }
    }

    // Internal function: return the character at the current index.
    fn peek(&self) -> char {
        match self.data.chars().nth(self.index) {
            Some(c) => c,
            None => '\x00',
        }
    }

    // Internal function: return the character at the current index
    // and increment the index by one.
    fn advance(&mut self) -> char {
        let c = self.peek();
        if !self.is_eof() {
            self.index += 1;
        }
        if c == '\n' {
            self.curr_pos.line += 1;
            self.curr_pos.col = 1;
        } else {
            self.curr_pos.col += 1
        }
        c
    }

    // Internal function: verify if the end of the program has been
    // reached.
    fn is_eof(&self) -> bool {
        self.index >= self.data.len()
    }

    pub fn next_token(&mut self) -> Result<Token, Error> {
        self.skip_comments_and_whitespace();

        if self.is_eof() {
            return Ok(self.empty_tok(TokenType::Eof));
        }

        match self.peek() {
            '+' => { self.advance(); Ok(self.empty_tok(TokenType::Plus)) }
            '-' => { self.advance(); Ok(self.empty_tok(TokenType::Minus)) }
            '*' => { self.advance(); Ok(self.empty_tok(TokenType::Star)) }
            '/' => { self.advance(); Ok(self.empty_tok(TokenType::Slash)) }
            '=' => { self.advance(); Ok(self.empty_tok(TokenType::Equal)) }
            '(' => { self.advance(); Ok(self.empty_tok(TokenType::LParen)) }
            ')' => { self.advance(); Ok(self.empty_tok(TokenType::RParen)) }
            ':' => { self.advance(); Ok(self.empty_tok(TokenType::Colon)) }
            ';' => { self.advance(); Ok(self.empty_tok(TokenType::Semicolon)) }
            c if c.is_digit(10) => { self.scan_int_or_float() }
            c if is_id_start(c) => { self.scan_id_or_keyword() }
            c   => { Err(Error::IllegalCharacter(self.curr_pos, c)) }
        }
    }

    fn scan_int_or_float(&mut self) -> Result<Token, Error> {
        let mut val = String::new();
        while self.peek().is_digit(10) {
            val.push(self.advance());
        }

        if self.peek() != '.' {
            return Ok(self.valued_tok(TokenType::Int, val));
        }

        val.push(self.advance()); // Add decimal point.

        while self.peek().is_digit(10) {
            val.push(self.advance());
        }

        Ok(self.valued_tok(TokenType::Float, val))
    }

    fn scan_id_or_keyword(&mut self) -> Result<Token, Error> {
        let mut lexeme = String::new();
        while is_id_char(self.peek()) {
            lexeme.push(self.advance());
        }

        if lexeme == "if" { return Ok(self.empty_tok(TokenType::If)); }
        if lexeme == "then" { return Ok(self.empty_tok(TokenType::Then)); }
        if lexeme == "else" { return Ok(self.empty_tok(TokenType::Else)); }
        if lexeme == "end" { return Ok(self.empty_tok(TokenType::End)); }
        if lexeme == "while" { return Ok(self.empty_tok(TokenType::While)); }
        if lexeme == "do" { return Ok(self.empty_tok(TokenType::Do)); }
        if lexeme == "done" { return Ok(self.empty_tok(TokenType::Done)); }
        if lexeme == "read" { return Ok(self.empty_tok(TokenType::Read)); }
        if lexeme == "print" { return Ok(self.empty_tok(TokenType::Print)); }
        if lexeme == "var" { return Ok(self.empty_tok(TokenType::Var)); }
        if lexeme == "int" { return Ok(self.empty_tok(TokenType::TypeInt)); }
        if lexeme == "float" { return Ok(self.empty_tok(TokenType::TypeFloat)); }

        Ok(self.valued_tok(TokenType::Id, lexeme))
    }

    fn skip_comments_and_whitespace(&mut self) {
        loop {
            self.skip_whitespace();
            if self.peek() == '#' {
                self.skip_comment();
            } else {
                break;
            }
        }
    }

    fn skip_whitespace(&mut self) {
        while self.peek().is_whitespace() {
            self.advance();
        }
    }

    fn skip_comment(&mut self) {
        while self.peek() != '\n' {
            self.advance();
        }
    }

    fn empty_tok(&mut self, t: TokenType) -> Token {
        let t = Token {
            typ: t,
            lexeme: None,
            pos: self.start_pos,
        };
        self.start_pos = self.curr_pos;
        t
    }

    fn valued_tok(&mut self, t: TokenType, v: String) -> Token {
        let t = Token {
            typ: t,
            lexeme: Some(v),
            pos: self.start_pos,
        };
        self.start_pos = self.curr_pos;
        t
    }
}

fn is_id_start(c: char) -> bool {
    (c >= 'a' && c <= 'z') ||
    c == '_' ||
    (c >= 'A' && c <= 'Z')
}

fn is_id_char(c: char) -> bool {
    (c >= 'a' && c <= 'z') ||
    c == '_' ||
    c.is_digit(10) ||
    (c >= 'A' && c <= 'Z')
}
<commit_msg>Fix position information with whitespaces.<commit_after>use error::Error;
use pos::Pos;
use token::{Token, TokenType};

pub struct Scanner {
    data: String,
    index: usize,
    start_pos: Pos,
    curr_pos: Pos,
}

impl Scanner {
    // Create a new scanner from a given program represented as a
    // String.
    pub fn new(data: String) -> Self {
        Scanner {
            data: data,
            index: 0,
            start_pos: Pos { line: 1, col: 1 },
            curr_pos: Pos { line: 1, col: 1 },
        }
    }

    // Internal function: return the character at the current index.
    fn peek(&self) -> char {
        match self.data.chars().nth(self.index) {
            Some(c) => c,
            None => '\x00',
        }
    }

    // Internal function: return the character at the current index
    // and increment the index by one.
    fn advance(&mut self) -> char {
        let c = self.peek();
        if !self.is_eof() {
            self.index += 1;
        }
        if c == '\n' {
            self.curr_pos.line += 1;
            self.curr_pos.col = 1;
        } else {
            self.curr_pos.col += 1
        }
        c
    }

    // Internal function: verify if the end of the program has been
    // reached.
    fn is_eof(&self) -> bool {
        self.index >= self.data.len()
    }

    pub fn next_token(&mut self) -> Result<Token, Error> {
        // Discard blanks and comments.
        self.skip_comments_and_whitespace();

        // Set start_pos (starting position of the next token) to the
        // value of curr_pos (current position in the text stream).
        self.start_pos = self.curr_pos;

        // Return Eof if the end of the file has been reached.
        if self.is_eof() {
            return Ok(self.empty_tok(TokenType::Eof));
        }

        // Scanning dispatch.
        match self.peek() {
            '+' => { self.advance(); Ok(self.empty_tok(TokenType::Plus)) }
            '-' => { self.advance(); Ok(self.empty_tok(TokenType::Minus)) }
            '*' => { self.advance(); Ok(self.empty_tok(TokenType::Star)) }
            '/' => { self.advance(); Ok(self.empty_tok(TokenType::Slash)) }
            '=' => { self.advance(); Ok(self.empty_tok(TokenType::Equal)) }
            '(' => { self.advance(); Ok(self.empty_tok(TokenType::LParen)) }
            ')' => { self.advance(); Ok(self.empty_tok(TokenType::RParen)) }
            ':' => { self.advance(); Ok(self.empty_tok(TokenType::Colon)) }
            ';' => { self.advance(); Ok(self.empty_tok(TokenType::Semicolon)) }
            c if c.is_digit(10) => { self.scan_int_or_float() }
            c if is_id_start(c) => { self.scan_id_or_keyword() }
            c   => { Err(Error::IllegalCharacter(self.curr_pos, c)) }
        }
    }

    // Scan digits into an Int or Float token.
    fn scan_int_or_float(&mut self) -> Result<Token, Error> {
        let mut val = String::new();
        while self.peek().is_digit(10) {
            val.push(self.advance());
        }

        if self.peek() != '.' {
            return Ok(self.valued_tok(TokenType::Int, val));
        }

        val.push(self.advance()); // Add decimal point.

        while self.peek().is_digit(10) {
            val.push(self.advance());
        }

        Ok(self.valued_tok(TokenType::Float, val))
    }

    // Scan alpha-numeric characters into an Id or a keyword token.
    fn scan_id_or_keyword(&mut self) -> Result<Token, Error> {
        let mut lexeme = String::new();
        while is_id_char(self.peek()) {
            lexeme.push(self.advance());
        }

        if lexeme == "if" { return Ok(self.empty_tok(TokenType::If)); }
        if lexeme == "then" { return Ok(self.empty_tok(TokenType::Then)); }
        if lexeme == "else" { return Ok(self.empty_tok(TokenType::Else)); }
        if lexeme == "end" { return Ok(self.empty_tok(TokenType::End)); }
        if lexeme == "while" { return Ok(self.empty_tok(TokenType::While)); }
        if lexeme == "do" { return Ok(self.empty_tok(TokenType::Do)); }
        if lexeme == "done" { return Ok(self.empty_tok(TokenType::Done)); }
        if lexeme == "read" { return Ok(self.empty_tok(TokenType::Read)); }
        if lexeme == "print" { return Ok(self.empty_tok(TokenType::Print)); }
        if lexeme == "var" { return Ok(self.empty_tok(TokenType::Var)); }
        if lexeme == "int" { return Ok(self.empty_tok(TokenType::TypeInt)); }
        if lexeme == "float" { return Ok(self.empty_tok(TokenType::TypeFloat)); }

        Ok(self.valued_tok(TokenType::Id, lexeme))
    }

    fn skip_comments_and_whitespace(&mut self) {
        loop {
            self.skip_whitespace();
            if self.peek() == '#' {
                self.skip_comment();
            } else {
                break;
            }
        }
    }

    fn skip_whitespace(&mut self) {
        while self.peek().is_whitespace() {
            self.advance();
        }
    }

    fn skip_comment(&mut self) {
        while self.peek() != '\n' {
            self.advance();
        }
    }

    fn empty_tok(&mut self, t: TokenType) -> Token {
        let t = Token {
            typ: t,
            lexeme: None,
            pos: self.start_pos,
        };
        t
    }

    fn valued_tok(&mut self, t: TokenType, v: String) -> Token {
        let t = Token {
            typ: t,
            lexeme: Some(v),
            pos: self.start_pos,
        };
        t
    }
}

fn is_id_start(c: char) -> bool {
    (c >= 'a' && c <= 'z') ||
    c == '_' ||
    (c >= 'A' && c <= 'Z')
}

fn is_id_char(c: char) -> bool {
    (c >= 'a' && c <= 'z') ||
    c == '_' ||
    c.is_digit(10) ||
    (c >= 'A' && c <= 'Z')
}
<|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.
//
// This file is heavily based on the rustc implementation of a multiple-producer, single-consumer
// channel. See copyright above. I have removed parts unnecessary to my uses and added those that
// I needed.

use mpmc::{LockFreeQueue};

use std::cmp;
use std::isize;

use std::sync::atomic::{Ordering, AtomicIsize};
use std::sync::{Condvar, Mutex, MutexGuard};

const DISCONNECTED: isize = isize::MIN;
const FUDGE: isize = 1024;
#[cfg(test)]
const MAX_STEALS: isize = 5;
#[cfg(not(test))]
const MAX_STEALS: isize = 1 << 20;

pub struct Canal<T> {
    queue: LockFreeQueue<T>,
    cnt: AtomicIsize, // How many items are on this channel
    steals: isize, // How many times has a port received without blocking?

    should_block: Mutex<bool>, // tells ports that they should block
    block_condvar: Condvar, // CondVar associated with should_block, how send() wakes up blocked ports

    // The number of channels which are currently using this packet.
    channels: AtomicIsize,
    // The number of ports which are currently using this packet.
    ports: AtomicIsize,

    sender_drain: AtomicIsize,
}

/// Failure modes for receiving on the port.
#[derive(Debug)]
pub enum Failure {
    /// There is nothing to recieve.
    Empty,
    /// The port is disconnected.
    Disconnected,
}

impl<T> Canal<T> {
   pub fn new(cap: usize) -> Canal<T> {
        Canal {
            queue: LockFreeQueue::with_capacity(cap),
            cnt: AtomicIsize::new(0),
            steals: 0,
            should_block: Mutex::new(true),
            block_condvar: Condvar::new(),
            // TODO: Why did these AtomicIsize have an initialization of 2 before?
            channels: AtomicIsize::new(1),
            ports: AtomicIsize::new(1),
            sender_drain: AtomicIsize::new(0),
        }
    }
}

impl<T: Send> Canal<T> {
    // pub fn send(&self, val: T) -> Result<(), T> {
    //     self.queue.push(val)
    // }

    // pub fn recv(&self) -> Option<T> {
    //     self.queue.pop()
    // }

    pub fn send(&self, t: T) -> Result<(), T> {
        // See Port::drop for what's going on
        if self.ports.load(Ordering::SeqCst) == 0 { return Err(t) }

        // Note that the multiple sender case is a little trickier
        // semantically than the single sender case. The logic for
        // incrementing is "add and if disconnected store disconnected".
        // This could end up leading some senders to believe that there
        // wasn't a disconnect if in fact there was a disconnect. This means
        // that while one thread is attempting to re-store the disconnected
        // states, other threads could walk through merrily incrementing
        // this very-negative disconnected count. To prevent senders from
        // spuriously attempting to send when the channels is actually
        // disconnected, the count has a ranged check here.
        //
        // This is also done for another reason. Remember that the return
        // value of this function is:
        //
        //  `true` == the data *may* be received, this essentially has no
        //            meaning
        //  `false` == the data will *never* be received, this has a lot of
        //             meaning
        //
        // In the SPSC case, we have a check of 'queue.is_empty()' to see
        // whether the data was actually received, but this same condition
        // means nothing in a multi-producer context. As a result, this
        // preflight check serves as the definitive "this will never be
        // received". Once we get beyond this check, we have permanently
        // entered the realm of "this may be received"
        if self.cnt.load(Ordering::SeqCst) < DISCONNECTED + FUDGE {
            return Err(t)
        }

        try!(self.queue.push(t));
        match self.cnt.fetch_add(1, Ordering::SeqCst) {
            -1 => {
                self.wake_port();
            }

            // In this case, we have possibly failed to send our data, and
            // we need to consider re-popping the data in order to fully
            // destroy it. We must arbitrate among the multiple senders,
            // however, because the queues that we're using are
            // single-consumer queues. In order to do this, all exiting
            // pushers will use an atomic count in order to count those
            // flowing through. Pushers who see 0 are required to drain as
            // much as possible, and then can only exit when they are the
            // only pusher (otherwise they must try again).
            n if n < DISCONNECTED + FUDGE => {
                // see the comment in 'try' for a shared channel for why this
                // window of "not disconnected" is ok.
                println!("Storing DISCONNECTED");
                self.cnt.store(DISCONNECTED, Ordering::SeqCst);

                if self.sender_drain.fetch_add(1, Ordering::SeqCst) == 0 {
                    loop {
                        // drain the queue, for info on the thread yield see the
                        // discussion in try_recv
                        loop {
                            match self.queue.pop() {
                                Some(..) => {}
                                None => break,
                            }
                        }
                        // maybe we're done, if we're not the last ones
                        // here, then we need to go try again.
                        if self.sender_drain.fetch_sub(1, Ordering::SeqCst) == 1 {
                            break
                        }
                    }

                    // At this point, there may still be data on the queue,
                    // but only if the count hasn't been incremented and
                    // some other sender hasn't finished pushing data just
                    // yet. That sender in question will drain its own data.
                }
            }

            // Can't make any assumptions about this case like in the SPSC case.
            _ => {}
        }

        Ok(())
    }

    pub fn recv(&mut self) -> Result<T, Failure> {
        // This code is essentially the exact same as that found in the stream
        // case (see stream.rs)
        match self.try_recv() {
            Err(Failure::Empty) => {}
            data => return data,
        }

        if let Some((mut should_block, condvar)) = self.decrement() {
            while *should_block {
                should_block = condvar.wait(should_block).unwrap();
            }
        }

        match self.try_recv() {
            data @ Ok(..) => { self.steals -= 1; data }
            data => data,
        }
    }

    // Essentially the exact same thing as the stream decrement function.
    // Returns Some((guard, condvar)) if blocking should proceed.
    fn decrement<'a>(&'a mut self) -> Option<(MutexGuard<'a, bool>, &'a Condvar)> {
        let block = self.should_block.lock().unwrap();

        let steals = self.steals;
        self.steals = 0;

        match self.cnt.fetch_sub(1 + steals, Ordering::SeqCst) {
            DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); }
            // If we factor in our steals and notice that the channel has no
            // data, we successfully sleep
            n => {
                assert!(n >= 0);
                if n - steals <= 0 { return Some((block, &self.block_condvar)) }
            }
        }

        None
    }

    pub fn try_recv(&mut self) -> Result<T, Failure> {
        match self.queue.pop() {
            // See the discussion in the stream implementation for why we
            // might decrement steals.
            Some(data) => {
                if self.steals > MAX_STEALS {
                    match self.cnt.swap(0, Ordering::SeqCst) {
                        DISCONNECTED => {
                            self.cnt.store(DISCONNECTED, Ordering::SeqCst);
                        }
                        n => {
                            let m = cmp::min(n, self.steals);
                            self.steals -= m;
                            self.bump(n - m);
                        }
                    }
                    assert!(self.steals >= 0);
                }
                self.steals += 1;
                Ok(data)
            }

            // See the discussion in the stream implementation for why we try
            // again.
            None => {
                match self.cnt.load(Ordering::SeqCst) {
                    n if n != DISCONNECTED => Err(Failure::Empty),
                    _ => {
                        match self.queue.pop() {
                            Some(t) => Ok(t),
                            None => Err(Failure::Disconnected),
                        }
                    }
                }
            }
        }
    }

    fn bump(&self, amt: isize) -> isize {
        match self.cnt.fetch_add(amt, Ordering::SeqCst) {
            DISCONNECTED => {
                self.cnt.store(DISCONNECTED, Ordering::SeqCst);
                DISCONNECTED
            }
            n => n
        }
    }

    // Prepares this shared packet for a channel clone, essentially just bumping
    // a refcount.
    pub fn clone_chan(&self) {
        self.channels.fetch_add(1, Ordering::SeqCst);
    }

    // [@chrino]
    // Prepares this shared packet for a channel clone, essentially just bumping
    // a refcount.
    pub fn clone_port(&self) {
        self.ports.fetch_add(1, Ordering::SeqCst);
    }

    // Decrement the reference count on a channel. This is called whenever a
    // Chan is dropped and may end up waking up a receiver. It's the receiver's
    // responsibility on the other end to figure out that we've disconnected.
    pub fn drop_chan(&self) {
        match self.channels.fetch_sub(1, Ordering::SeqCst) {
            1 => {}
            n if n > 1 => return,
            n => panic!("bad number of channels left {}", n),
        }

        match self.cnt.swap(DISCONNECTED, Ordering::SeqCst) {
            -1 => { self.wake_port(); }
            DISCONNECTED => {}
            n => { assert!(n >= 0); }
        }
    }

    // See the long discussion inside of stream.rs for why the queue is drained,
    // and why it is done in this fashion.
    //
    // [@chrino]
    // Decrement ports and only drain on last port
    pub fn drop_port(&self) {
        match self.ports.fetch_sub(1, Ordering::SeqCst) {
            1 => {}
            n if n > 1 => return,
            n => panic!("bad number of channels left {}", n),
        }

        let mut steals = self.steals;
        while {
            let cnt = self.cnt.compare_and_swap(steals, DISCONNECTED, Ordering::SeqCst);
            cnt != DISCONNECTED && cnt != steals
        } {
            // See the discussion in 'try_recv' for why we yield
            // control of this thread.
            loop {
                match self.queue.pop() {
                    Some(..) => { steals += 1; }
                    None => break,
                }
            }
        }
    }

    // Set the should_block mutex to false and notify a single port. The corresponding port
    // being woken should ensure that should_block is set correctly after being woken up and
    // responding to changes.
    fn wake_port(&self) {
        let mut block = self.should_block.lock().unwrap();
        *block = false;
        self.block_condvar.notify_one();
    }
}

impl<T> Drop for Canal<T> {
    fn drop(&mut self) {
        assert_eq!(self.cnt.load(Ordering::SeqCst), DISCONNECTED);
        assert_eq!(self.channels.load(Ordering::SeqCst), 0);
        assert_eq!(self.ports.load(Ordering::SeqCst), 0);
    }
}

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

    #[test]
    fn test_send_recv() {
        let mut canal = Canal::new(20);

        for i in 0..20 {
            assert!(canal.send(i as u8).is_ok());
        }

        for _i in 0..20 {
            let popped = canal.recv().unwrap();
            let mut found = false;
            for x in 0..20 {
                if popped == x {
                    found = true
                }
            }
            assert!(found);
        }

        canal.drop_port();
        canal.drop_chan();
    }
}
<commit_msg>More tests and fix bugs<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.
//
// This file is heavily based on the rustc implementation of a multiple-producer, single-consumer
// channel. See copyright above. I have removed parts unnecessary to my uses and added those that
// I needed.

use mpmc::{LockFreeQueue};

use std::cmp;
use std::isize;

use std::sync::atomic::{Ordering, AtomicIsize};
use std::sync::{Condvar, Mutex, MutexGuard};

const DISCONNECTED: isize = isize::MIN;
const FUDGE: isize = 1024;
#[cfg(test)]
const MAX_STEALS: isize = 5;
#[cfg(not(test))]
const MAX_STEALS: isize = 1 << 20;

pub struct Canal<T> {
    queue: LockFreeQueue<T>,
    cnt: AtomicIsize, // How many items are on this channel
    // TODO: Make this atomic since we now have many receivers
    steals: isize, // How many times has a port received without blocking?

    should_block: Mutex<bool>, // tells ports that they should block
    block_condvar: Condvar, // CondVar associated with should_block, how send() wakes up blocked ports

    // The number of channels which are currently using this packet.
    channels: AtomicIsize,
    // The number of ports which are currently using this packet.
    ports: AtomicIsize,

    sender_drain: AtomicIsize,
}

/// Failure modes for receiving on the port.
#[derive(Debug)]
pub enum Failure {
    /// There is nothing to recieve.
    Empty,
    /// The port is disconnected.
    Disconnected,
}

impl<T> Canal<T> {
   pub fn new(cap: usize) -> Canal<T> {
        Canal {
            queue: LockFreeQueue::with_capacity(cap),
            cnt: AtomicIsize::new(0),
            steals: 0,
            should_block: Mutex::new(false),
            block_condvar: Condvar::new(),
            // TODO: Why did these AtomicIsize have an initialization of 2 before?
            channels: AtomicIsize::new(1),
            ports: AtomicIsize::new(1),
            sender_drain: AtomicIsize::new(0),
        }
    }
}

impl<T: Send> Canal<T> {
    // pub fn send(&self, val: T) -> Result<(), T> {
    //     self.queue.push(val)
    // }

    // pub fn recv(&self) -> Option<T> {
    //     self.queue.pop()
    // }

    pub fn send(&self, t: T) -> Result<(), T> {
        // See Port::drop for what's going on
        if self.ports.load(Ordering::SeqCst) == 0 { return Err(t) }

        // Note that the multiple sender case is a little trickier
        // semantically than the single sender case. The logic for
        // incrementing is "add and if disconnected store disconnected".
        // This could end up leading some senders to believe that there
        // wasn't a disconnect if in fact there was a disconnect. This means
        // that while one thread is attempting to re-store the disconnected
        // states, other threads could walk through merrily incrementing
        // this very-negative disconnected count. To prevent senders from
        // spuriously attempting to send when the channels is actually
        // disconnected, the count has a ranged check here.
        //
        // This is also done for another reason. Remember that the return
        // value of this function is:
        //
        //  `true` == the data *may* be received, this essentially has no
        //            meaning
        //  `false` == the data will *never* be received, this has a lot of
        //             meaning
        //
        // In the SPSC case, we have a check of 'queue.is_empty()' to see
        // whether the data was actually received, but this same condition
        // means nothing in a multi-producer context. As a result, this
        // preflight check serves as the definitive "this will never be
        // received". Once we get beyond this check, we have permanently
        // entered the realm of "this may be received"
        if self.cnt.load(Ordering::SeqCst) < DISCONNECTED + FUDGE {
            return Err(t)
        }

        try!(self.queue.push(t));
        match self.cnt.fetch_add(1, Ordering::SeqCst) {
            // Because multiple consumers and be decrementing cnt at once,
            // we do a ranged check here as well. We use the same fudge as
            // the DISCONNECT case in order to make sure the two cases do
            // not end up colliding with each other.
            //
            // The two cases are inherently racy, so we cannot ensure exact
            // numbers for the cnt in either case, thus we keep them far apart.
            //
            // TODO: This in essence limits the number of ports we can have. We would
            // like to have an arbitrary amount.
            n if n <= -1 && n > -1 - FUDGE  => {
                self.wake_port();
            }

            // In this case, we have possibly failed to send our data, and
            // we need to consider re-popping the data in order to fully
            // destroy it. We must arbitrate among the multiple senders,
            // however, because the queues that we're using are
            // single-consumer queues. In order to do this, all exiting
            // pushers will use an atomic count in order to count those
            // flowing through. Pushers who see 0 are required to drain as
            // much as possible, and then can only exit when they are the
            // only pusher (otherwise they must try again).
            n if n < DISCONNECTED + FUDGE => {
                // see the comment in 'try' for a shared channel for why this
                // window of "not disconnected" is ok.
                self.cnt.store(DISCONNECTED, Ordering::SeqCst);

                if self.sender_drain.fetch_add(1, Ordering::SeqCst) == 0 {
                    loop {
                        // drain the queue, for info on the thread yield see the
                        // discussion in try_recv
                        loop {
                            match self.queue.pop() {
                                Some(..) => {}
                                None => break,
                            }
                        }
                        // maybe we're done, if we're not the last ones
                        // here, then we need to go try again.
                        if self.sender_drain.fetch_sub(1, Ordering::SeqCst) == 1 {
                            break
                        }
                    }

                    // At this point, there may still be data on the queue,
                    // but only if the count hasn't been incremented and
                    // some other sender hasn't finished pushing data just
                    // yet. That sender in question will drain its own data.
                }
            }

            // Can't make any assumptions about this case like in the SPSC case.
            _ => {}
        }

        Ok(())
    }

    pub fn recv(&mut self) -> Result<T, Failure> {
        // This code is essentially the exact same as that found in the stream
        // case (see stream.rs)
        match self.try_recv() {
            Err(Failure::Empty) => {}
            data => return data,
        }

        if let Some((mut should_block, condvar)) = self.decrement() {
            while *should_block {
                should_block = condvar.wait(should_block).unwrap();
            }
        }

        match self.try_recv() {
            data @ Ok(..) => { self.steals -= 1; data }
            data => data,
        }
    }

    // Essentially the exact same thing as the stream decrement function.
    // Returns Some((guard, condvar)) if blocking should proceed.
    fn decrement<'a>(&'a mut self) -> Option<(MutexGuard<'a, bool>, &'a Condvar)> {
        let mut block = self.should_block.lock().expect("Lock was posioned");

        let steals = self.steals;
        self.steals = 0;

        // A -1 cnt value means that we need to wake someone up/block
        match self.cnt.fetch_sub(1 + steals, Ordering::SeqCst) {
            DISCONNECTED => { self.cnt.store(DISCONNECTED, Ordering::SeqCst); }
            // If we factor in our steals and notice that the channel has no
            // data, we successfully sleep
            n => {
                assert!(n > -1 - FUDGE); // See send() for more details.
                if n - steals <= 0 {
                    *block = true;
                    return Some((block, &self.block_condvar))
                }
            }
        }

        None
    }

    pub fn try_recv(&mut self) -> Result<T, Failure> {
        match self.queue.pop() {
            // See the discussion in the stream implementation for why we
            // might decrement steals.
            Some(data) => {
                if self.steals > MAX_STEALS {
                    match self.cnt.swap(0, Ordering::SeqCst) {
                        DISCONNECTED => {
                            self.cnt.store(DISCONNECTED, Ordering::SeqCst);
                        }
                        n => {
                            let m = cmp::min(n, self.steals);
                            self.steals -= m;
                            self.bump(n - m);
                        }
                    }
                    assert!(self.steals >= 0);
                }
                self.steals += 1;
                Ok(data)
            }

            // See the discussion in the stream implementation for why we try
            // again.
            None => {
                match self.cnt.load(Ordering::SeqCst) {
                    n if n != DISCONNECTED => Err(Failure::Empty),
                    _ => {
                        match self.queue.pop() {
                            Some(t) => Ok(t),
                            None => Err(Failure::Disconnected),
                        }
                    }
                }
            }
        }
    }

    fn bump(&self, amt: isize) -> isize {
        match self.cnt.fetch_add(amt, Ordering::SeqCst) {
            DISCONNECTED => {
                self.cnt.store(DISCONNECTED, Ordering::SeqCst);
                DISCONNECTED
            }
            n => n
        }
    }

    // Prepares this shared packet for a channel clone, essentially just bumping
    // a refcount.
    pub fn clone_chan(&self) {
        self.channels.fetch_add(1, Ordering::SeqCst);
    }

    // [@chrino]
    // Prepares this shared packet for a channel clone, essentially just bumping
    // a refcount.
    pub fn clone_port(&self) {
        self.ports.fetch_add(1, Ordering::SeqCst);
    }

    // Decrement the reference count on a channel. This is called whenever a
    // Chan is dropped and may end up waking up a receiver. It's the receiver's
    // responsibility on the other end to figure out that we've disconnected.
    pub fn drop_chan(&self) {
        match self.channels.fetch_sub(1, Ordering::SeqCst) {
            1 => {}
            n if n > 1 => return,
            n => panic!("bad number of channels left {}", n),
        }

        match self.cnt.swap(DISCONNECTED, Ordering::SeqCst) {
            -1 => { self.wake_port(); }
            DISCONNECTED => {}
            n => { assert!(n >= 0); }
        }
    }

    // See the long discussion inside of stream.rs for why the queue is drained,
    // and why it is done in this fashion.
    //
    // [@chrino]
    // Decrement ports and only drain on last port
    pub fn drop_port(&self) {
        match self.ports.fetch_sub(1, Ordering::SeqCst) {
            1 => {}
            n if n > 1 => return,
            n => panic!("bad number of channels left {}", n),
        }

        let mut steals = self.steals;
        while {
            let cnt = self.cnt.compare_and_swap(steals, DISCONNECTED, Ordering::SeqCst);
            cnt != DISCONNECTED && cnt != steals
        } {
            // See the discussion in 'try_recv' for why we yield
            // control of this thread.
            loop {
                match self.queue.pop() {
                    Some(..) => { steals += 1; }
                    None => break,
                }
            }
        }
    }

    // Set the should_block mutex to false and notify a single port. The corresponding port
    // being woken should ensure that should_block is set correctly after being woken up and
    // responding to changes.
    fn wake_port(&self) {
        let mut block = self.should_block.lock().unwrap();
        *block = false;
        self.block_condvar.notify_one();
    }
}

impl<T> Drop for Canal<T> {
    fn drop(&mut self) {
        assert_eq!(self.cnt.load(Ordering::SeqCst), DISCONNECTED);
        assert_eq!(self.channels.load(Ordering::SeqCst), 0);
        assert_eq!(self.ports.load(Ordering::SeqCst), 0);
    }
}

#[cfg(test)]
mod tests {
    use super::Canal;
    use mpmc::mpmc_channel;
    use std::thread;
    use std::sync::{Arc, Barrier};

    #[test]
    fn test_send_recv() {
        let mut canal = Canal::new(20);

        for i in 0..20 {
            assert!(canal.send(i as u8).is_ok());
        }

        for _i in 0..20 {
            let popped = canal.recv().unwrap();
            let mut found = false;
            for x in 0..20 {
                if popped == x {
                    found = true
                }
            }
            assert!(found);
        }

        canal.drop_port();
        canal.drop_chan();
    }

    #[test]
    fn test_send_full() {
        let canal = Canal::new(2);
        assert!(canal.send(1 as u8).is_ok());
        assert!(canal.send(2 as u8).is_ok());
        assert!(canal.send(3 as u8).is_err());
        canal.drop_port();
        canal.drop_chan();
    }

    #[test]
    fn test_blocking() {
        let (sn, rc) = mpmc_channel(5);

        let barrier = Arc::new(Barrier::new(6));
        let mut recv_vec = Vec::new();
        for _i in 0..5 {
            let b = barrier.clone();
            let r = rc.clone();
            recv_vec.push(thread::spawn(move || {
                b.wait();
                let popped = r.recv().unwrap();
                let mut found = false;
                for x in 0..5 {
                    if popped == x {
                        found = true
                    }
                }
                assert!(found);
            }));
        }

        barrier.wait(); // Wait for all threads to start.
        thread::yield_now(); // yeild current thread to ensure recv's block
        for i in 0..5 {
            sn.send(i as u8).unwrap();
        }

        for thr in recv_vec.into_iter() {
            thr.join().unwrap();
        }
    }
}
<|endoftext|>