<commit_before>use common::util::*;

extern crate uu_stat;
pub use self::uu_stat::*;

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

    #[test]
    fn test_access() {
        assert_eq!("drwxr-xr-x", pretty_access(S_IFDIR | 0o755));
        assert_eq!("-rw-r--r--", pretty_access(S_IFREG | 0o644));
        assert_eq!("srw-r-----", pretty_access(S_IFSOCK | 0o640));
        assert_eq!("lrw-r-xr-x", pretty_access(S_IFLNK | 0o655));
        assert_eq!("?rw-r-xr-x", pretty_access(0o655));

        assert_eq!("brwSr-xr-x",
                   pretty_access(S_IFBLK | S_ISUID as mode_t | 0o655));
        assert_eq!("brwsr-xr-x",
                   pretty_access(S_IFBLK | S_ISUID as mode_t | 0o755));

        assert_eq!("prw---sr--",
                   pretty_access(S_IFIFO | S_ISGID as mode_t | 0o614));
        assert_eq!("prw---Sr--",
                   pretty_access(S_IFIFO | S_ISGID as mode_t | 0o604));

        assert_eq!("c---r-xr-t",
                   pretty_access(S_IFCHR | S_ISVTX as mode_t | 0o055));
        assert_eq!("c---r-xr-T",
                   pretty_access(S_IFCHR | S_ISVTX as mode_t | 0o054));
    }

    #[test]
    fn test_file_type() {
        assert_eq!("block special file", pretty_filetype(S_IFBLK, 0));
        assert_eq!("character special file", pretty_filetype(S_IFCHR, 0));
        assert_eq!("regular file", pretty_filetype(S_IFREG, 1));
        assert_eq!("regular empty file", pretty_filetype(S_IFREG, 0));
        assert_eq!("weird file", pretty_filetype(0, 0));
    }

    #[test]
    fn test_fs_type() {
        assert_eq!("ext2/ext3", pretty_fstype(0xEF53));
        assert_eq!("tmpfs", pretty_fstype(0x01021994));
        assert_eq!("nfs", pretty_fstype(0x6969));
        assert_eq!("btrfs", pretty_fstype(0x9123683e));
        assert_eq!("xfs", pretty_fstype(0x58465342));
        assert_eq!("zfs", pretty_fstype(0x2FC12FC1));
        assert_eq!("ntfs", pretty_fstype(0x5346544e));
        assert_eq!("fat", pretty_fstype(0x4006));
        assert_eq!("UNKNOWN (0x1234)", pretty_fstype(0x1234));
    }
}

#[test]
fn test_scanutil() {
    assert_eq!(Some((-5, 2)), "-5zxc".scan_num::<i32>());
    assert_eq!(Some((51, 2)), "51zxc".scan_num::<u32>());
    assert_eq!(Some((192, 4)), "+192zxc".scan_num::<i32>());
    assert_eq!(None, "z192zxc".scan_num::<i32>());

    assert_eq!(Some(('a', 3)), "141zxc".scan_char(8));
    assert_eq!(Some(('\n', 2)), "12qzxc".scan_char(8));
    assert_eq!(Some(('\r', 1)), "dqzxc".scan_char(16));
    assert_eq!(None, "z2qzxc".scan_char(8));
}

#[test]
fn test_groupnum() {
    assert_eq!("12,379,821,234", group_num("12379821234"));
    assert_eq!("21,234", group_num("21234"));
    assert_eq!("821,234", group_num("821234"));
    assert_eq!("1,821,234", group_num("1821234"));
    assert_eq!("1,234", group_num("1234"));
    assert_eq!("234", group_num("234"));
    assert_eq!("24", group_num("24"));
    assert_eq!("4", group_num("4"));
    assert_eq!("", group_num(""));
}

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

    #[test]
    fn normal_format() {
        let s = "%'010.2ac%-#5.w\n";
        let expected = vec![Token::Directive {
                                flag: F_GROUP | F_ZERO,
                                width: 10,
                                precision: 2,
                                format: 'a',
                            },
                            Token::Char('c'),
                            Token::Directive {
                                flag: F_LEFT | F_ALTER,
                                width: 5,
                                precision: 0,
                                format: 'w',
                            },
                            Token::Char('\n')];
        assert_eq!(&expected, &Stater::generate_tokens(s, false).unwrap());
    }

    #[test]
    fn printf_format() {
        let s = "%-# 15a\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.-23w\\x12\\167\\132\\112\\n";
        let expected = vec![Token::Directive {
                                flag: F_LEFT | F_ALTER | F_SPACE,
                                width: 15,
                                precision: -1,
                                format: 'a',
                            },
                            Token::Char('\r'),
                            Token::Char('"'),
                            Token::Char('\\'),
                            Token::Char('\x07'),
                            Token::Char('\x08'),
                            Token::Char('\x1B'),
                            Token::Char('\x0C'),
                            Token::Char('\x0B'),
                            Token::Directive {
                                flag: F_SIGN | F_ZERO,
                                width: 20,
                                precision: -1,
                                format: 'w',
                            },
                            Token::Char('\x12'),
                            Token::Char('w'),
                            Token::Char('Z'),
                            Token::Char('J'),
                            Token::Char('\n')];
        assert_eq!(&expected, &Stater::generate_tokens(s, true).unwrap());
    }
}

#[test]
fn test_invalid_option() {
    new_ucmd!()
        .arg("-w").arg("-q").arg("/").fails();
}

#[cfg(target_os = "linux")]
const NORMAL_FMTSTR: &'static str = "%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s %u %U %w %W %x %X %y %Y %z %Z";
#[cfg(target_os = "linux")]
const DEV_FMTSTR: &'static str = "%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (%t/%T) %u %U %w %W %x %X %y %Y %z %Z";
#[cfg(target_os = "linux")]
const FS_FMTSTR: &'static str = "%a %b %c %d %f %i %l %n %s %S %t %T";

#[test]
#[cfg(target_os = "linux")]
fn test_terse_fs_format() {
    let args = ["-f", "-t", "/proc"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_fs_format() {
    let args = ["-f", "-c", FS_FMTSTR, "/dev/shm"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_terse_normal_format() {
    let args = ["-t", "/"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_normal_format() {
    let args = ["-c", NORMAL_FMTSTR, "/boot"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_follow_symlink() {
    let args = ["-L", "-c", DEV_FMTSTR, "/dev/cdrom"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_symlink() {
    let args = ["-c", DEV_FMTSTR, "/dev/cdrom"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_char() {
    let args = ["-c", DEV_FMTSTR, "/dev/zero"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_multi_files() {
    let args = ["-c", NORMAL_FMTSTR, "/dev", "/usr/lib", "/etc/fstab", "/var"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_printf() {
    let args = ["--printf=123%-# 15q\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.23m\\x12\\167\\132\\112\\n", "/"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[cfg(target_os = "linux")]
fn expected_result(args: &[&str]) -> String {
    use std::process::Command;

    let output = Command::new(util_name!()).args(args).output().unwrap();
    String::from_utf8_lossy(&output.stdout).into_owned()
}
<commit_msg>stat: fix test on travis<commit_after>use common::util::*;

extern crate uu_stat;
pub use self::uu_stat::*;

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

    #[test]
    fn test_access() {
        assert_eq!("drwxr-xr-x", pretty_access(S_IFDIR | 0o755));
        assert_eq!("-rw-r--r--", pretty_access(S_IFREG | 0o644));
        assert_eq!("srw-r-----", pretty_access(S_IFSOCK | 0o640));
        assert_eq!("lrw-r-xr-x", pretty_access(S_IFLNK | 0o655));
        assert_eq!("?rw-r-xr-x", pretty_access(0o655));

        assert_eq!("brwSr-xr-x",
                   pretty_access(S_IFBLK | S_ISUID as mode_t | 0o655));
        assert_eq!("brwsr-xr-x",
                   pretty_access(S_IFBLK | S_ISUID as mode_t | 0o755));

        assert_eq!("prw---sr--",
                   pretty_access(S_IFIFO | S_ISGID as mode_t | 0o614));
        assert_eq!("prw---Sr--",
                   pretty_access(S_IFIFO | S_ISGID as mode_t | 0o604));

        assert_eq!("c---r-xr-t",
                   pretty_access(S_IFCHR | S_ISVTX as mode_t | 0o055));
        assert_eq!("c---r-xr-T",
                   pretty_access(S_IFCHR | S_ISVTX as mode_t | 0o054));
    }

    #[test]
    fn test_file_type() {
        assert_eq!("block special file", pretty_filetype(S_IFBLK, 0));
        assert_eq!("character special file", pretty_filetype(S_IFCHR, 0));
        assert_eq!("regular file", pretty_filetype(S_IFREG, 1));
        assert_eq!("regular empty file", pretty_filetype(S_IFREG, 0));
        assert_eq!("weird file", pretty_filetype(0, 0));
    }

    #[test]
    fn test_fs_type() {
        assert_eq!("ext2/ext3", pretty_fstype(0xEF53));
        assert_eq!("tmpfs", pretty_fstype(0x01021994));
        assert_eq!("nfs", pretty_fstype(0x6969));
        assert_eq!("btrfs", pretty_fstype(0x9123683e));
        assert_eq!("xfs", pretty_fstype(0x58465342));
        assert_eq!("zfs", pretty_fstype(0x2FC12FC1));
        assert_eq!("ntfs", pretty_fstype(0x5346544e));
        assert_eq!("fat", pretty_fstype(0x4006));
        assert_eq!("UNKNOWN (0x1234)", pretty_fstype(0x1234));
    }
}

#[test]
fn test_scanutil() {
    assert_eq!(Some((-5, 2)), "-5zxc".scan_num::<i32>());
    assert_eq!(Some((51, 2)), "51zxc".scan_num::<u32>());
    assert_eq!(Some((192, 4)), "+192zxc".scan_num::<i32>());
    assert_eq!(None, "z192zxc".scan_num::<i32>());

    assert_eq!(Some(('a', 3)), "141zxc".scan_char(8));
    assert_eq!(Some(('\n', 2)), "12qzxc".scan_char(8));
    assert_eq!(Some(('\r', 1)), "dqzxc".scan_char(16));
    assert_eq!(None, "z2qzxc".scan_char(8));
}

#[test]
fn test_groupnum() {
    assert_eq!("12,379,821,234", group_num("12379821234"));
    assert_eq!("21,234", group_num("21234"));
    assert_eq!("821,234", group_num("821234"));
    assert_eq!("1,821,234", group_num("1821234"));
    assert_eq!("1,234", group_num("1234"));
    assert_eq!("234", group_num("234"));
    assert_eq!("24", group_num("24"));
    assert_eq!("4", group_num("4"));
    assert_eq!("", group_num(""));
}

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

    #[test]
    fn normal_format() {
        let s = "%'010.2ac%-#5.w\n";
        let expected = vec![Token::Directive {
                                flag: F_GROUP | F_ZERO,
                                width: 10,
                                precision: 2,
                                format: 'a',
                            },
                            Token::Char('c'),
                            Token::Directive {
                                flag: F_LEFT | F_ALTER,
                                width: 5,
                                precision: 0,
                                format: 'w',
                            },
                            Token::Char('\n')];
        assert_eq!(&expected, &Stater::generate_tokens(s, false).unwrap());
    }

    #[test]
    fn printf_format() {
        let s = "%-# 15a\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.-23w\\x12\\167\\132\\112\\n";
        let expected = vec![Token::Directive {
                                flag: F_LEFT | F_ALTER | F_SPACE,
                                width: 15,
                                precision: -1,
                                format: 'a',
                            },
                            Token::Char('\r'),
                            Token::Char('"'),
                            Token::Char('\\'),
                            Token::Char('\x07'),
                            Token::Char('\x08'),
                            Token::Char('\x1B'),
                            Token::Char('\x0C'),
                            Token::Char('\x0B'),
                            Token::Directive {
                                flag: F_SIGN | F_ZERO,
                                width: 20,
                                precision: -1,
                                format: 'w',
                            },
                            Token::Char('\x12'),
                            Token::Char('w'),
                            Token::Char('Z'),
                            Token::Char('J'),
                            Token::Char('\n')];
        assert_eq!(&expected, &Stater::generate_tokens(s, true).unwrap());
    }
}

#[test]
fn test_invalid_option() {
    new_ucmd!()
        .arg("-w").arg("-q").arg("/").fails();
}

#[cfg(target_os = "linux")]
const NORMAL_FMTSTR: &'static str = "%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s %u %U %w %W %x %X %y %Y %z %Z";
#[cfg(target_os = "linux")]
const DEV_FMTSTR: &'static str = "%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (%t/%T) %u %U %w %W %x %X %y %Y %z %Z";
#[cfg(target_os = "linux")]
const FS_FMTSTR: &'static str = "%a %b %c %d %f %i %l %n %s %S %t %T";

#[test]
#[cfg(target_os = "linux")]
fn test_terse_fs_format() {
    let args = ["-f", "-t", "/proc"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_fs_format() {
    let args = ["-f", "-c", FS_FMTSTR, "/dev/shm"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_terse_normal_format() {
    let args = ["-t", "/"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_normal_format() {
    let args = ["-c", NORMAL_FMTSTR, "/boot"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_follow_symlink() {
    let args = ["-L", "-c", DEV_FMTSTR, "/dev/cdrom"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_symlink() {
    let args = ["-c", DEV_FMTSTR, "/dev/cdrom"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_char() {
    let args = ["-c", DEV_FMTSTR, "/dev/pts/ptmx"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_multi_files() {
    let args = ["-c", NORMAL_FMTSTR, "/dev", "/usr/lib", "/etc/fstab", "/var"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[test]
#[cfg(target_os = "linux")]
fn test_printf() {
    let args = ["--printf=123%-# 15q\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.23m\\x12\\167\\132\\112\\n", "/"];
    new_ucmd!().args(&args)
        .run()
        .stdout_is(expected_result(&args));
}

#[cfg(target_os = "linux")]
fn expected_result(args: &[&str]) -> String {
    use std::process::Command;

    let output = Command::new(util_name!()).args(args).output().unwrap();
    String::from_utf8_lossy(&output.stdout).into_owned()
}
<|endoftext|>


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

//! Module for searching text.

use std::cmp::{min,max};

use xi_rope::delta::{Delta, DeltaRegion};
use xi_rope::find::{find, is_multiline_regex, CaseMatching};
use xi_rope::rope::{Rope, LinesMetric, RopeInfo};
use xi_rope::tree::Cursor;
use xi_rope::interval::Interval;
use selection::{Selection, SelRegion};
use xi_rope::tree::Metric;
use regex::{RegexBuilder, Regex};
use word_boundaries::WordCursor;

const REGEX_SIZE_LIMIT: usize = 1000000;

/// Information about search queries and number of matches for find
#[derive(Serialize, Deserialize, Debug)]
pub struct FindStatus {
    /// The current search query.
    chars: Option<String>,

    /// Whether the active search is case matching.
    case_sensitive: Option<bool>,

    /// Whether the search query is considered as regular expression.
    is_regex: Option<bool>,

    /// Query only matches whole words.
    whole_words: Option<bool>,

    /// Total number of matches.
    matches: usize
}

/// Contains logic to search text
pub struct Find {
    // todo: link to search query so that search results can be correlated back to query

    /// The occurrences, which determine the highlights, have been updated.
    hls_dirty: bool,
    /// The currently active search string
    search_string: Option<String>,
    /// The case matching setting for the currently active search
    case_matching: CaseMatching,
    /// The search query should be considered as regular expression
    regex: Option<Regex>,
    /// Query matches only whole words.
    whole_words: bool,
    /// The set of all known find occurrences (highlights)
    occurrences: Selection,
}

impl Find {
    pub fn new() -> Find {
        Find {
            hls_dirty: true,
            search_string: None,
            case_matching: CaseMatching::CaseInsensitive,
            regex: None,
            whole_words: false,
            occurrences: Selection::new(),
        }
    }

    pub fn occurrences(&self) -> &Selection {
        &self.occurrences
    }

    pub fn hls_dirty(&self) -> bool {
        self.hls_dirty
    }

    pub fn find_status(&self, matches_only: bool) -> FindStatus {
        if matches_only {
            FindStatus {
                chars: None,
                case_sensitive: None,
                is_regex: None,
                whole_words: None,
                matches: self.occurrences.len(),
            }
        } else {
            FindStatus {
                chars: self.search_string.clone(),
                case_sensitive: Some(self.case_matching == CaseMatching::Exact),
                is_regex: Some(self.regex.is_some()),
                whole_words: Some(self.whole_words),
                matches: self.occurrences.len(),
            }
        }
    }

    pub fn set_hls_dirty(&mut self, is_dirty: bool) {
        self.hls_dirty = is_dirty
    }

    pub fn update_highlights(&mut self, text: &Rope, delta: &Delta<RopeInfo>) {
        // update search highlights for changed regions
        if self.search_string.is_some() {
            // invalidate occurrences around deletion positions
            for DeltaRegion{ old_offset, new_offset: _, len } in delta.iter_deletions() {
                self.occurrences.delete_range(old_offset, old_offset + len, false);
            }

            self.occurrences = self.occurrences.apply_delta(delta, false, false);

            // invalidate occurrences around insert positions
            for DeltaRegion{ new_offset, len, .. } in delta.iter_inserts() {
                // also invalidate previous occurrence since it might expand after insertion
                // eg. for regex .* every insertion after match will be part of match
                self.occurrences.delete_range(new_offset.checked_sub(1).unwrap_or(0), new_offset + len, false);
            }

            // update find for the whole delta and everything after
            let (iv, _) = delta.summary();

            // get last valid occurrence that was unaffected by the delta
            let start = match self.occurrences.regions_in_range(0, iv.start()).last() {
                Some(reg) => reg.end,
                None => 0
            };

            // invalidate all search results from the point of the last valid search result until ...
            let is_multi_line = LinesMetric::next(self.search_string.as_ref().unwrap(), 0).is_some();
            let is_multi_line_regex = self.regex.is_some() && is_multiline_regex(self.search_string.as_ref().unwrap());

            if is_multi_line || is_multi_line_regex {
                // ... the end of the file
                self.occurrences.delete_range(iv.start(), text.len(), false);
                self.update_find(text, start, text.len(), false);
            } else {
                // ... the end of the line including line break
                let mut cursor = Cursor::new(&text, iv.start());
                let end_of_line = match cursor.next::<LinesMetric>() {
                    Some(end) => end,
                    None if cursor.pos() == text.len() => cursor.pos(),
                    _ => return
                };

                self.occurrences.delete_range(iv.start(), end_of_line, false);
                self.update_find(text, start, end_of_line, false);
            }
        }
    }

    /// Set search parameters and executes the search.
    pub fn do_find(&mut self, text: &Rope, search_string: String, case_sensitive: bool,
                   is_regex: bool, whole_words: bool) {
        if search_string.len() == 0 {
            self.unset();
        }

        self.set_find(&search_string, case_sensitive, is_regex, whole_words);
        self.update_find(text, 0, text.len(), false);
    }

    /// Unsets the search and removes all highlights from the view.
    pub fn unset(&mut self) {
        self.search_string = None;
        self.occurrences = Selection::new();
        self.hls_dirty = true;
    }

    /// Sets find parameters and search query.
    fn set_find(&mut self, search_string: &str, case_sensitive: bool, is_regex: bool, whole_words: bool) {
        let case_matching = if case_sensitive {
            CaseMatching::Exact
        } else {
            CaseMatching::CaseInsensitive
        };

        if let Some(ref s) = self.search_string {
            if s == search_string && case_matching == self.case_matching &&
                self.regex.is_some() == is_regex && self.whole_words == whole_words {
                // search parameters did not change
                return;
            }
        }

        self.unset();

        self.search_string = Some(search_string.to_string());
        self.case_matching = case_matching;
        self.whole_words = whole_words;

        // create regex from untrusted input
        self.regex = match is_regex {
            false => None,
            true => {
                RegexBuilder::new(search_string)
                    .size_limit(REGEX_SIZE_LIMIT)
                    .case_insensitive(case_matching == CaseMatching::CaseInsensitive)
                    .build()
                    .ok()
            }
        };
    }

    /// Execute the search on the provided text in the range provided by `start` and `end`.
    pub fn update_find(&mut self, text: &Rope, start: usize, end: usize, include_slop: bool)
    {
        if self.search_string.is_none() {
            return;
        }

        // extend the search by twice the string length (twice, because case matching may increase
        // the length of an occurrence)
        let slop = if include_slop { self.search_string.as_ref().unwrap().len() * 2 } else { 0 };

        let search_string = self.search_string.as_ref().unwrap();

        // expand region to be able to find occurrences around the region's edges
        let from = max(start, slop) - slop;
        let to = min(end + slop, text.len());

        // TODO: this interval might cut a unicode codepoint, make sure it is
        // aligned to codepoint boundaries.
        let sub_text = text.subseq(Interval::new_closed_open(0, to));
        let mut find_cursor = Cursor::new(&sub_text, from);
        let mut raw_lines = text.lines_raw(from, to);

        while let Some(start) = find(&mut find_cursor, &mut raw_lines, self.case_matching,
                                     &search_string, &self.regex) {
            let end = find_cursor.pos();

            if self.whole_words && !self.is_matching_whole_words(text, start, end) {
                raw_lines = text.lines_raw(find_cursor.pos(), to);
                continue;
            }

            let region = SelRegion::new(start, end);
            let (_, e) = self.occurrences.add_range_distinct(region);
            // in case of ambiguous search results (e.g. search "aba" in "ababa"),
            // the search result closer to the beginning of the file wins
            if e != end {
                // Skip the search result and keep the occurrence that is closer to
                // the beginning of the file. Re-align the cursor to the kept
                // occurrence
                find_cursor.set(e);
                raw_lines = text.lines_raw(find_cursor.pos(), to);
                continue;
            }

            // in case current cursor matches search result (for example query a* matches)
            // all cursor positions, then cursor needs to be increased so that search
            // continues at next position. Otherwise, search will result in overflow since
            // search will always repeat at current cursor position.
            if start == end {
                // determine whether end of text is reached and stop search or increase
                // cursor manually
                if end + 1 >= text.len() {
                    break;
                } else {
                    find_cursor.set(end + 1);
                }
            }

            // update line iterator so that line starts at current cursor position
            raw_lines = text.lines_raw(find_cursor.pos(), to);
        }

        self.hls_dirty = true;
    }

    /// Return the occurrence closest to the provided selection `sel`. If searched is reversed then
    /// the occurrence closest to the start of the selection is returned. `wrapped` indicates that
    /// if the end of the text is reached the search continues from the start.
    pub fn next_occurrence(&self, text: &Rope, reverse: bool, wrapped: bool, sel: &Selection) -> Option<SelRegion> {
        if self.occurrences.len() == 0 {
            return None;
        }

        let (sel_start, sel_end) = match sel.last() {
            Some(last) if last.is_caret() =>
                // if last selection is caret then allow the current position to be part of the occurrence
                (last.min(), last.max()),
            Some(last) if !last.is_caret() =>
                // if the last selection is not a caret then continue searching after the caret
                (last.min(), last.max() + 1),
            _ => (0, 0)
        };

        if reverse {
            let next_occurrence = match sel_start.checked_sub(1) {
                Some(search_end) => self.occurrences.regions_in_range(0, search_end).last(),
                None => None
            };

            if next_occurrence.is_none() && !wrapped {
                // get previous unselected occurrence
                return self.occurrences.regions_in_range(0, text.len()).iter().cloned().filter(|o| {
                    sel.regions_in_range(o.min(), o.max()).is_empty()
                }).collect::<Vec<SelRegion>>().last().cloned();
            }

            next_occurrence.cloned()
        } else {
            let next_occurrence = self.occurrences.regions_in_range(sel_end, text.len()).first();

            if next_occurrence.is_none() && !wrapped {
                // get next unselected occurrence
                return self.occurrences.regions_in_range(0, text.len()).iter().cloned().filter(|o| {
                    sel.regions_in_range(o.min(), o.max()).is_empty()
                }).collect::<Vec<SelRegion>>().first().cloned();
            }

            next_occurrence.cloned()
        }
    }

    /// Checks if the start and end of a match is matching whole words.
    fn is_matching_whole_words(&self, text: &Rope, start: usize, end: usize) -> bool {
        let mut word_end_cursor = WordCursor::new(text, end - 1);
        let mut word_start_cursor = WordCursor::new(text, start + 1);

        if let Some(start_boundary) = word_start_cursor.prev_boundary() {
            if start_boundary != start {
                return false;
            }
        }

        if let Some(end_boundary) = word_end_cursor.next_boundary() {
            if end_boundary != end {
                return false;
            }
        }

        true
    }
}<commit_msg>Update find highlights for multiple edits at the same time<commit_after>// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Module for searching text.

use std::cmp::{min,max};

use xi_rope::delta::{Delta, DeltaRegion};
use xi_rope::find::{find, is_multiline_regex, CaseMatching};
use xi_rope::rope::{Rope, LinesMetric, RopeInfo};
use xi_rope::tree::Cursor;
use xi_rope::interval::Interval;
use selection::{Selection, SelRegion};
use xi_rope::tree::Metric;
use regex::{RegexBuilder, Regex};
use word_boundaries::WordCursor;

const REGEX_SIZE_LIMIT: usize = 1000000;

/// Information about search queries and number of matches for find
#[derive(Serialize, Deserialize, Debug)]
pub struct FindStatus {
    /// The current search query.
    chars: Option<String>,

    /// Whether the active search is case matching.
    case_sensitive: Option<bool>,

    /// Whether the search query is considered as regular expression.
    is_regex: Option<bool>,

    /// Query only matches whole words.
    whole_words: Option<bool>,

    /// Total number of matches.
    matches: usize
}

/// Contains logic to search text
pub struct Find {
    // todo: link to search query so that search results can be correlated back to query

    /// The occurrences, which determine the highlights, have been updated.
    hls_dirty: bool,
    /// The currently active search string
    search_string: Option<String>,
    /// The case matching setting for the currently active search
    case_matching: CaseMatching,
    /// The search query should be considered as regular expression
    regex: Option<Regex>,
    /// Query matches only whole words.
    whole_words: bool,
    /// The set of all known find occurrences (highlights)
    occurrences: Selection,
}

impl Find {
    pub fn new() -> Find {
        Find {
            hls_dirty: true,
            search_string: None,
            case_matching: CaseMatching::CaseInsensitive,
            regex: None,
            whole_words: false,
            occurrences: Selection::new(),
        }
    }

    pub fn occurrences(&self) -> &Selection {
        &self.occurrences
    }

    pub fn hls_dirty(&self) -> bool {
        self.hls_dirty
    }

    pub fn find_status(&self, matches_only: bool) -> FindStatus {
        if matches_only {
            FindStatus {
                chars: None,
                case_sensitive: None,
                is_regex: None,
                whole_words: None,
                matches: self.occurrences.len(),
            }
        } else {
            FindStatus {
                chars: self.search_string.clone(),
                case_sensitive: Some(self.case_matching == CaseMatching::Exact),
                is_regex: Some(self.regex.is_some()),
                whole_words: Some(self.whole_words),
                matches: self.occurrences.len(),
            }
        }
    }

    pub fn set_hls_dirty(&mut self, is_dirty: bool) {
        self.hls_dirty = is_dirty
    }

    pub fn update_highlights(&mut self, text: &Rope, delta: &Delta<RopeInfo>) {
        // update search highlights for changed regions
        if self.search_string.is_some() {
            // invalidate occurrences around deletion positions
            for DeltaRegion{ old_offset, new_offset: _, len } in delta.iter_deletions() {
                self.occurrences.delete_range(old_offset, old_offset + len, false);
            }

            self.occurrences = self.occurrences.apply_delta(delta, false, false);

            // invalidate occurrences around insert positions
            for DeltaRegion{ new_offset, len, .. } in delta.iter_inserts() {
                // also invalidate previous occurrence since it might expand after insertion
                // eg. for regex .* every insertion after match will be part of match
                self.occurrences.delete_range(new_offset.checked_sub(1).unwrap_or(0), new_offset + len, false);
            }

            // update find for the whole delta and everything after
            let (iv, _) = delta.summary();

            // get last valid occurrence that was unaffected by the delta
            let start = match self.occurrences.regions_in_range(0, iv.start()).last() {
                Some(reg) => reg.end,
                None => 0
            };

            // invalidate all search results from the point of the last valid search result until ...
            let is_multi_line = LinesMetric::next(self.search_string.as_ref().unwrap(), 0).is_some();
            let is_multi_line_regex = self.regex.is_some() && is_multiline_regex(self.search_string.as_ref().unwrap());

            if is_multi_line || is_multi_line_regex {
                // ... the end of the file
                self.occurrences.delete_range(iv.start(), text.len(), false);
                self.update_find(text, start, text.len(), false);
            } else {
                // ... the end of the line including line break
                let mut cursor = Cursor::new(&text, iv.end());

                let end_of_line = match cursor.next::<LinesMetric>() {
                    Some(end) => end,
                    None if cursor.pos() == text.len() => cursor.pos(),
                    _ => return
                };

                self.occurrences.delete_range(iv.start(), end_of_line, false);
                self.update_find(text, start, end_of_line, false);
            }
        }
    }

    /// Set search parameters and executes the search.
    pub fn do_find(&mut self, text: &Rope, search_string: String, case_sensitive: bool,
                   is_regex: bool, whole_words: bool) {
        if search_string.len() == 0 {
            self.unset();
        }

        self.set_find(&search_string, case_sensitive, is_regex, whole_words);
        self.update_find(text, 0, text.len(), false);
    }

    /// Unsets the search and removes all highlights from the view.
    pub fn unset(&mut self) {
        self.search_string = None;
        self.occurrences = Selection::new();
        self.hls_dirty = true;
    }

    /// Sets find parameters and search query.
    fn set_find(&mut self, search_string: &str, case_sensitive: bool, is_regex: bool, whole_words: bool) {
        let case_matching = if case_sensitive {
            CaseMatching::Exact
        } else {
            CaseMatching::CaseInsensitive
        };

        if let Some(ref s) = self.search_string {
            if s == search_string && case_matching == self.case_matching &&
                self.regex.is_some() == is_regex && self.whole_words == whole_words {
                // search parameters did not change
                return;
            }
        }

        self.unset();

        self.search_string = Some(search_string.to_string());
        self.case_matching = case_matching;
        self.whole_words = whole_words;

        // create regex from untrusted input
        self.regex = match is_regex {
            false => None,
            true => {
                RegexBuilder::new(search_string)
                    .size_limit(REGEX_SIZE_LIMIT)
                    .case_insensitive(case_matching == CaseMatching::CaseInsensitive)
                    .build()
                    .ok()
            }
        };
    }

    /// Execute the search on the provided text in the range provided by `start` and `end`.
    pub fn update_find(&mut self, text: &Rope, start: usize, end: usize, include_slop: bool)
    {
        if self.search_string.is_none() {
            return;
        }

        // extend the search by twice the string length (twice, because case matching may increase
        // the length of an occurrence)
        let slop = if include_slop { self.search_string.as_ref().unwrap().len() * 2 } else { 0 };

        let search_string = self.search_string.as_ref().unwrap();

        // expand region to be able to find occurrences around the region's edges
        let from = max(start, slop) - slop;
        let to = min(end + slop, text.len());

        // TODO: this interval might cut a unicode codepoint, make sure it is
        // aligned to codepoint boundaries.
        let sub_text = text.subseq(Interval::new_closed_open(0, to));
        let mut find_cursor = Cursor::new(&sub_text, from);
        let mut raw_lines = text.lines_raw(from, to);

        while let Some(start) = find(&mut find_cursor, &mut raw_lines, self.case_matching,
                                     &search_string, &self.regex) {
            let end = find_cursor.pos();

            if self.whole_words && !self.is_matching_whole_words(text, start, end) {
                raw_lines = text.lines_raw(find_cursor.pos(), to);
                continue;
            }

            let region = SelRegion::new(start, end);
            let (_, e) = self.occurrences.add_range_distinct(region);
            // in case of ambiguous search results (e.g. search "aba" in "ababa"),
            // the search result closer to the beginning of the file wins
            if e != end {
                // Skip the search result and keep the occurrence that is closer to
                // the beginning of the file. Re-align the cursor to the kept
                // occurrence
                find_cursor.set(e);
                raw_lines = text.lines_raw(find_cursor.pos(), to);
                continue;
            }

            // in case current cursor matches search result (for example query a* matches)
            // all cursor positions, then cursor needs to be increased so that search
            // continues at next position. Otherwise, search will result in overflow since
            // search will always repeat at current cursor position.
            if start == end {
                // determine whether end of text is reached and stop search or increase
                // cursor manually
                if end + 1 >= text.len() {
                    break;
                } else {
                    find_cursor.set(end + 1);
                }
            }

            // update line iterator so that line starts at current cursor position
            raw_lines = text.lines_raw(find_cursor.pos(), to);
        }

        self.hls_dirty = true;
    }

    /// Return the occurrence closest to the provided selection `sel`. If searched is reversed then
    /// the occurrence closest to the start of the selection is returned. `wrapped` indicates that
    /// if the end of the text is reached the search continues from the start.
    pub fn next_occurrence(&self, text: &Rope, reverse: bool, wrapped: bool, sel: &Selection) -> Option<SelRegion> {
        if self.occurrences.len() == 0 {
            return None;
        }

        let (sel_start, sel_end) = match sel.last() {
            Some(last) if last.is_caret() =>
                // if last selection is caret then allow the current position to be part of the occurrence
                (last.min(), last.max()),
            Some(last) if !last.is_caret() =>
                // if the last selection is not a caret then continue searching after the caret
                (last.min(), last.max() + 1),
            _ => (0, 0)
        };

        if reverse {
            let next_occurrence = match sel_start.checked_sub(1) {
                Some(search_end) => self.occurrences.regions_in_range(0, search_end).last(),
                None => None
            };

            if next_occurrence.is_none() && !wrapped {
                // get previous unselected occurrence
                return self.occurrences.regions_in_range(0, text.len()).iter().cloned().filter(|o| {
                    sel.regions_in_range(o.min(), o.max()).is_empty()
                }).collect::<Vec<SelRegion>>().last().cloned();
            }

            next_occurrence.cloned()
        } else {
            let next_occurrence = self.occurrences.regions_in_range(sel_end, text.len()).first();

            if next_occurrence.is_none() && !wrapped {
                // get next unselected occurrence
                return self.occurrences.regions_in_range(0, text.len()).iter().cloned().filter(|o| {
                    sel.regions_in_range(o.min(), o.max()).is_empty()
                }).collect::<Vec<SelRegion>>().first().cloned();
            }

            next_occurrence.cloned()
        }
    }

    /// Checks if the start and end of a match is matching whole words.
    fn is_matching_whole_words(&self, text: &Rope, start: usize, end: usize) -> bool {
        let mut word_end_cursor = WordCursor::new(text, end - 1);
        let mut word_start_cursor = WordCursor::new(text, start + 1);

        if let Some(start_boundary) = word_start_cursor.prev_boundary() {
            if start_boundary != start {
                return false;
            }
        }

        if let Some(end_boundary) = word_end_cursor.next_boundary() {
            if end_boundary != end {
                return false;
            }
        }

        true
    }
}<|endoftext|>


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

#![feature(test)]

extern crate test;
extern crate xi_rope;

use test::Bencher;
use xi_rope::tree::*;

fn build_triangle(n: usize) -> String {
    let mut s = String::new();
    let mut line = String::new();
    for _ in 0..n {
        s += &line;
        s += "\n";
        line += "a";
    }
    s
}

fn build_short_lines(n: usize) -> String {
    let line = "match s.as_bytes()[minsplit - 1..splitpoint].iter().rposition(|&c| c == b'\n') {";
    let mut s = String::new();
    for _ in 0..n {
        s += line;
    }
    s
}

fn build_few_big_lines(size: usize) -> String {
    let mut s = String::with_capacity(size * 10 + 20);
    for _ in 0..10 {
        for _ in 0..size {
            s += "a";
        }
        s += "\n";
    }
    s
}

#[bench]
fn build_tree_triangle(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str(&build_triangle(500)));
}

#[bench]
fn build_tree_short_lines(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str(&build_short_lines(1_000)));
}

#[bench]
fn build_tree_few_short_lines(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str(&build_few_big_lines(1_000)));
}

#[bench]
fn build_tree_triangle_stack(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str_stacked(&build_triangle(500)));
}

#[bench]
fn build_tree_short_lines_stack(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str_stacked(&build_short_lines(1_000)));
}

#[bench]
fn build_tree_few_short_lines_stack(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str_stacked(&build_few_big_lines(1_000)));
}
<commit_msg>naming: few_small_lines does not exist<commit_after>// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(test)]

extern crate test;
extern crate xi_rope;

use test::Bencher;
use xi_rope::tree::*;

fn build_triangle(n: usize) -> String {
    let mut s = String::new();
    let mut line = String::new();
    for _ in 0..n {
        s += &line;
        s += "\n";
        line += "a";
    }
    s
}

fn build_short_lines(n: usize) -> String {
    let line = "match s.as_bytes()[minsplit - 1..splitpoint].iter().rposition(|&c| c == b'\n') {";
    let mut s = String::new();
    for _ in 0..n {
        s += line;
    }
    s
}

fn build_few_big_lines(size: usize) -> String {
    let mut s = String::with_capacity(size * 10 + 20);
    for _ in 0..10 {
        for _ in 0..size {
            s += "a";
        }
        s += "\n";
    }
    s
}

#[bench]
fn build_tree_triangle(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str(&build_triangle(500)));
}

#[bench]
fn build_tree_short_lines(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str(&build_short_lines(1_000)));
}

#[bench]
fn build_tree_few_big_lines(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str(&build_few_big_lines(1_000)));
}

#[bench]
fn build_tree_triangle_stack(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str_stacked(&build_triangle(500)));
}

#[bench]
fn build_tree_short_lines_stack(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str_stacked(&build_short_lines(1_000)));
}

#[bench]
fn build_tree_few_big_lines_stack(b: &mut Bencher) {
    let mut t = TreeBuilder::new();
    b.iter(|| t.push_str_stacked(&build_few_big_lines(1_000)));
}
<|endoftext|>


<commit_before>// Copyright 2016 Hroi Sigurdsson
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

//!
//! To use these tests and benchmarks, make sure test/ contains bgp4-dump.txt and
//! bgp6-dump.txt each containing a full dump of the current internet routing
//! table, one prefix per line in CIDR notation.
//!

extern crate rand;

use self::rand::{Rng};

use super::*;
use test::{Bencher,black_box};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use std::io::prelude::*;
use std::io::{BufReader, Error};
use std::fs::File;

lazy_static! {
    static ref FULL_BGP_TABLE_IDENT: IpLookupTable<Ipv4Addr,(Ipv4Addr, u32)> = {load_bgp_dump(0).unwrap()};
    static ref FULL_BGP_TABLE_UNIT: IpLookupTable<Ipv4Addr,()> = {load_bgp_dump_light(0).unwrap()};
    //static ref FULL_BGP6_TABLE_IDENT: Ipv6LookupTable<(Ipv6Addr, u32)> = {load_bgp6_dump(0).unwrap()};
    static ref FULL_BGP6_TABLE_UNIT: IpLookupTable<Ipv6Addr,()> = {load_bgp6_dump_light(0).unwrap()};
}

/// We store the the prefix in the value, so we can later compare it and check that it is the correct value for the key.
fn load_bgp6_dump_light(limit: u32) -> Result<IpLookupTable<Ipv6Addr,()>, Error> {
    let mut tbm = IpLookupTable::<Ipv6Addr,()>::with_capacity(512);
    let f = try!(File::open("test/bgp6-dump.txt"));
    let r = BufReader::new(f);
    let mut i = 0;
    for line in r.lines() {
        let line = line.unwrap();
        if let Some(slash_offset) = line.find('/') {
            i += 1;
            if limit > 0 && i > limit {
                break;
            }
            let ip = Ipv6Addr::from_str(&line[..slash_offset]).unwrap();
            let masklen = u32::from_str(&line[slash_offset+1..]).unwrap();
            assert!(masklen <= 128);
            tbm.insert(ip, masklen, ());
        }
    }
    Ok(tbm)
}

fn load_bgp_dump_light(limit: u32) -> Result<IpLookupTable<Ipv4Addr,()>, Error> {
    let mut tbl = IpLookupTable::<Ipv4Addr,()>::with_capacity(512);
    let f = try!(File::open("test/bgp4-dump.txt"));
    let r = BufReader::new(f);
    let mut i = 0;
    for line in r.lines() {
        let line = line.unwrap();
        if let Some(slash_offset) = line.find('/') {
            i += 1;
            if limit > 0 && i > limit {
                break;
            }
            let ip = Ipv4Addr::from_str(&line[..slash_offset]).unwrap();
            let masklen = u32::from_str(&line[slash_offset+1..]).unwrap();
            assert!(masklen <= 32);
            tbl.insert(ip, masklen, ());
        }
    }
    let (node_bytes, result_bytes) = tbl.mem_usage();
    println!("load_bgp_dump_light: nodes: {} bytes, results: {} bytes", node_bytes, result_bytes);
    Ok(tbl)
}

#[allow(dead_code)]
fn load_bgp6_dump(limit: u32) -> Result<IpLookupTable<Ipv6Addr, (Ipv6Addr, u32)>, Error> {
    let mut tbm = IpLookupTable::<Ipv6Addr,(Ipv6Addr,u32)>::with_capacity(512);
    let f = try!(File::open("test/bgp6-dump.txt"));
    let r = BufReader::new(f);
    let mut i = 0;
    for line in r.lines() {
        let line = line.unwrap();
        if let Some(slash_offset) = line.find('/') {
            i += 1;
            if limit > 0 && i > limit {
                break;
            }
            let ip = Ipv6Addr::from_str(&line[..slash_offset]).unwrap();
            let masklen = u32::from_str(&line[slash_offset+1..]).unwrap();
            assert!(masklen <= 128);
            tbm.insert(ip, masklen, (ip, masklen));
        }
    }
    //tbm.shrink_to_fit();
    Ok(tbm)
}

fn load_bgp_dump(limit: u32) -> Result<IpLookupTable<Ipv4Addr, (Ipv4Addr, u32)>, Error> {
    let mut tbm = IpLookupTable::<Ipv4Addr,(Ipv4Addr,u32)>::with_capacity(512);
    let f = try!(File::open("test/bgp4-dump.txt"));
    let r = BufReader::new(f);
    let mut i = 0;
    for line in r.lines() {
        let line = line.unwrap();
        if let Some(slash_offset) = line.find('/') {
            i += 1;
            if limit > 0 && i > limit {
                break;
            }
            let ip = Ipv4Addr::from_str(&line[..slash_offset]).unwrap();
            let masklen = u32::from_str(&line[slash_offset+1..]).unwrap();
            assert!(masklen <= 32);
            tbm.insert(ip, masklen, (ip, masklen));
        }
    }
    //tbm.shrink_to_fit();
    Ok(tbm)
}

#[test]
fn loadv4() {
    let tbm = load_bgp_dump_light(0).unwrap();
    let google_dns = Ipv4Addr::new(8,8,8,8);
    let (prefix, mask, _)= tbm.longest_match(google_dns).unwrap();
    assert_eq!(prefix, Ipv4Addr::new(8,8,8,0));
    assert_eq!(mask, 24);
}

#[bench]
fn iterv4(b: &mut Bencher) {
    b.iter(|| {
        for (prefix, masklen, value) in FULL_BGP_TABLE_IDENT.iter() {
            assert_eq!((prefix, masklen), *value);
        }
    });
}

#[test]
fn iterv4_print() {
    for (prefix, masklen, _) in FULL_BGP_TABLE_UNIT.iter() {
        println!("{}/{}", prefix, masklen);
    }
}

#[test]
fn loadv6() {
    let _ = load_bgp6_dump_light(0).unwrap();
}

#[test]
// check that the values returned match what was in the key
fn lookup_random_id_check() {
    let ref tbm = FULL_BGP_TABLE_IDENT;
    let mut rng = rand::weak_rng();
    for _ in 0..10000 {
        let ip = Ipv4Addr::from(rng.gen_range(1<<24, 224<<24));
        let result = tbm.longest_match(ip);
        println!("lookup({}) -> {:?}", ip, result);
        if let Some((prefix, masklen, val)) = result {
            let (orig_prefix, orig_masklen) = *val;
            assert_eq!((prefix, masklen), (orig_prefix, orig_masklen));
        }
    }
}

#[bench]
fn lookup_apple(b: &mut Bencher) {
    let ip = Ipv4Addr::new(17,151,0,151);
    b.iter(|| {
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn lookup_comcast6(b: &mut Bencher) {
    let ip = Ipv6Addr::from_str("2001:6c8:180:1::c3f9:1b20").unwrap();
    b.iter(|| {
        black_box(FULL_BGP6_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn lookup_netgroup(b: &mut Bencher) {
    let ip = Ipv4Addr::new(77,66,88,50);
    b.iter(|| {
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn lookup_googledns(b: &mut Bencher) {
    let ip = Ipv4Addr::new(8,8,8,8);
    b.iter(|| {
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn lookup_googledns6(b: &mut Bencher) {
    let ip = Ipv6Addr::from_str("2001:4860:4860::8888").unwrap();
    b.iter(|| {
        black_box(FULL_BGP6_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn localhost(b: &mut Bencher) {
    let ip = Ipv4Addr::new(127,0,0,1);
    b.iter(|| {
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn localhost6(b: &mut Bencher) {
    let ip = Ipv6Addr::from_str("::1").unwrap();
    b.iter(|| {
        black_box(FULL_BGP6_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn lookup_random_sample(b: &mut Bencher) {
    let mut rng = rand::weak_rng();
    let r: u32 = rng.gen_range(1<<24, 224<<24);
    let ip = Ipv4Addr::from(r);
    b.iter(||{
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    });
}

#[bench]
fn lookup_random_every(b: &mut Bencher) {
    let mut rng = rand::weak_rng();
    b.iter(||{
        let r: u32 = rng.gen_range(1<<24, 224<<24);
        let ip = Ipv4Addr::from(r);
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    });
}
<commit_msg>rename tests<commit_after>// Copyright 2016 Hroi Sigurdsson
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

//!
//! To use these tests and benchmarks, make sure test/ contains bgp4-dump.txt and
//! bgp6-dump.txt each containing a full dump of the current internet routing
//! table, one prefix per line in CIDR notation.
//!

extern crate rand;

use self::rand::{Rng};

use super::*;
use test::{Bencher,black_box};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use std::io::prelude::*;
use std::io::{BufReader, Error};
use std::fs::File;

lazy_static! {
    static ref FULL_BGP_TABLE_IDENT: IpLookupTable<Ipv4Addr,(Ipv4Addr, u32)> = {load_bgp_dump(0).unwrap()};
    static ref FULL_BGP_TABLE_UNIT: IpLookupTable<Ipv4Addr,()> = {load_bgp_dump_light(0).unwrap()};
    //static ref FULL_BGP6_TABLE_IDENT: Ipv6LookupTable<(Ipv6Addr, u32)> = {load_bgp6_dump(0).unwrap()};
    static ref FULL_BGP6_TABLE_UNIT: IpLookupTable<Ipv6Addr,()> = {load_bgp6_dump_light(0).unwrap()};
}

/// We store the the prefix in the value, so we can later compare it and check that it is the correct value for the key.
fn load_bgp6_dump_light(limit: u32) -> Result<IpLookupTable<Ipv6Addr,()>, Error> {
    let mut tbm = IpLookupTable::<Ipv6Addr,()>::with_capacity(512);
    let f = try!(File::open("test/bgp6-dump.txt"));
    let r = BufReader::new(f);
    let mut i = 0;
    for line in r.lines() {
        let line = line.unwrap();
        if let Some(slash_offset) = line.find('/') {
            i += 1;
            if limit > 0 && i > limit {
                break;
            }
            let ip = Ipv6Addr::from_str(&line[..slash_offset]).unwrap();
            let masklen = u32::from_str(&line[slash_offset+1..]).unwrap();
            assert!(masklen <= 128);
            tbm.insert(ip, masklen, ());
        }
    }
    Ok(tbm)
}

fn load_bgp_dump_light(limit: u32) -> Result<IpLookupTable<Ipv4Addr,()>, Error> {
    let mut tbl = IpLookupTable::<Ipv4Addr,()>::with_capacity(512);
    let f = try!(File::open("test/bgp4-dump.txt"));
    let r = BufReader::new(f);
    let mut i = 0;
    for line in r.lines() {
        let line = line.unwrap();
        if let Some(slash_offset) = line.find('/') {
            i += 1;
            if limit > 0 && i > limit {
                break;
            }
            let ip = Ipv4Addr::from_str(&line[..slash_offset]).unwrap();
            let masklen = u32::from_str(&line[slash_offset+1..]).unwrap();
            assert!(masklen <= 32);
            tbl.insert(ip, masklen, ());
        }
    }
    Ok(tbl)
}

#[allow(dead_code)]
fn load_bgp6_dump(limit: u32) -> Result<IpLookupTable<Ipv6Addr, (Ipv6Addr, u32)>, Error> {
    let mut tbm = IpLookupTable::<Ipv6Addr,(Ipv6Addr,u32)>::with_capacity(512);
    let f = try!(File::open("test/bgp6-dump.txt"));
    let r = BufReader::new(f);
    let mut i = 0;
    for line in r.lines() {
        let line = line.unwrap();
        if let Some(slash_offset) = line.find('/') {
            i += 1;
            if limit > 0 && i > limit {
                break;
            }
            let ip = Ipv6Addr::from_str(&line[..slash_offset]).unwrap();
            let masklen = u32::from_str(&line[slash_offset+1..]).unwrap();
            assert!(masklen <= 128);
            tbm.insert(ip, masklen, (ip, masklen));
        }
    }
    //tbm.shrink_to_fit();
    Ok(tbm)
}

fn load_bgp_dump(limit: u32) -> Result<IpLookupTable<Ipv4Addr, (Ipv4Addr, u32)>, Error> {
    let mut tbm = IpLookupTable::<Ipv4Addr,(Ipv4Addr,u32)>::with_capacity(512);
    let f = try!(File::open("test/bgp4-dump.txt"));
    let r = BufReader::new(f);
    let mut i = 0;
    for line in r.lines() {
        let line = line.unwrap();
        if let Some(slash_offset) = line.find('/') {
            i += 1;
            if limit > 0 && i > limit {
                break;
            }
            let ip = Ipv4Addr::from_str(&line[..slash_offset]).unwrap();
            let masklen = u32::from_str(&line[slash_offset+1..]).unwrap();
            assert!(masklen <= 32);
            tbm.insert(ip, masklen, (ip, masklen));
        }
    }
    //tbm.shrink_to_fit();
    Ok(tbm)
}

#[test]
fn loadv4() {
    let tbl = load_bgp_dump_light(0).unwrap();

    let (node_bytes, result_bytes) = tbl.mem_usage();
    println!("load_bgp_dump_light: nodes: {} bytes, results: {} bytes", node_bytes, result_bytes);

    let google_dns = Ipv4Addr::new(8,8,8,8);
    let (prefix, mask, _)= tbl.longest_match(google_dns).unwrap();
    assert_eq!(prefix, Ipv4Addr::new(8,8,8,0));
    assert_eq!(mask, 24);
}

#[bench]
fn iterv4(b: &mut Bencher) {
    b.iter(|| {
        for (prefix, masklen, value) in FULL_BGP_TABLE_IDENT.iter() {
            assert_eq!((prefix, masklen), *value);
        }
    });
}

#[test]
fn iterv4_print() {
    for (prefix, masklen, _) in FULL_BGP_TABLE_UNIT.iter() {
        println!("{}/{}", prefix, masklen);
    }
}

#[test]
fn loadv6() {
    let _ = load_bgp6_dump_light(0).unwrap();
}

#[test]
// check that the values returned match what was in the key
fn longest_match4_random_id_check() {
    let ref tbm = FULL_BGP_TABLE_IDENT;
    let mut rng = rand::weak_rng();
    for _ in 0..10000 {
        let ip = Ipv4Addr::from(rng.gen_range(1<<24, 224<<24));
        let result = tbm.longest_match(ip);
        println!("lookup({}) -> {:?}", ip, result);
        if let Some((prefix, masklen, val)) = result {
            let (orig_prefix, orig_masklen) = *val;
            assert_eq!((prefix, masklen), (orig_prefix, orig_masklen));
        }
    }
}

#[bench]
fn longest_match4_apple(b: &mut Bencher) {
    let ip = Ipv4Addr::new(17,151,0,151);
    b.iter(|| {
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn longest_match6_comcast(b: &mut Bencher) {
    let ip = Ipv6Addr::from_str("2001:6c8:180:1::c3f9:1b20").unwrap();
    b.iter(|| {
        black_box(FULL_BGP6_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn longest_match4_netgroup(b: &mut Bencher) {
    let ip = Ipv4Addr::new(77,66,88,50);
    b.iter(|| {
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn longest_match4_googledns(b: &mut Bencher) {
    let ip = Ipv4Addr::new(8,8,8,8);
    b.iter(|| {
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn longest_match6_googledns(b: &mut Bencher) {
    let ip = Ipv6Addr::from_str("2001:4860:4860::8888").unwrap();
    b.iter(|| {
        black_box(FULL_BGP6_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn longest_match4_localhost(b: &mut Bencher) {
    let ip = Ipv4Addr::new(127,0,0,1);
    b.iter(|| {
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn longest_match6_localhost(b: &mut Bencher) {
    let ip = Ipv6Addr::from_str("::1").unwrap();
    b.iter(|| {
        black_box(FULL_BGP6_TABLE_UNIT.longest_match(ip));
    })
}

#[bench]
fn longest_match4_random_sample(b: &mut Bencher) {
    let mut rng = rand::weak_rng();
    let r: u32 = rng.gen_range(1<<24, 224<<24);
    let ip = Ipv4Addr::from(r);
    b.iter(||{
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    });
}

#[bench]
fn longest_match4_random_every(b: &mut Bencher) {
    let mut rng = rand::weak_rng();
    b.iter(||{
        let r: u32 = rng.gen_range(1<<24, 224<<24);
        let ip = Ipv4Addr::from(r);
        black_box(FULL_BGP_TABLE_UNIT.longest_match(ip));
    });
}

#[bench]
fn exact_match4_googledns(b: &mut Bencher) {
    let ip = Ipv4Addr::new(8,8,8,0);
    b.iter(|| {
        black_box(FULL_BGP_TABLE_UNIT.exact_match(ip,24));
    })
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate tokio_core;
extern crate tokio_proto;
extern crate tokio_service;

use std::io;
use std::str;
use tokio_core::io::{Codec, EasyBuf};

pub struct LineCodec;

impl Codec for LineCodec {
    type In = String;
    type Out = String;

    fn decode(&mut self, buf: &mut EasyBuf) -> io::Result<Option<Self::In>> {
        if let Some(i) = buf.as_slice().iter().position(|&b| b == b'\n') {
            // remove the serialized frame from the buffer.
            let line = buf.drain_to(i);

            // Also remove the '\n'
            buf.drain_to(1);

            // Turn this data into a UTF string and return it in a Frame.
            match str::from_utf8(line.as_slice()) {
                Ok(s) => Ok(Some(s.to_string())),
                Err(_) => Err(io::Error::new(io::ErrorKind::Other, "invalid UTF-8")),
            }
        } else {
            Ok(None)
        }
    }

    fn encode(&mut self, msg: String, buf: &mut Vec<u8>) -> io::Result<()> {
        buf.extend(msg.as_bytes());
        buf.push(b'\n');
        Ok(())
    }
}

use tokio_proto::pipeline::ServerProto;

pub struct LineProto;

use tokio_core::io::{Io, Framed};

impl<T: Io + 'static> ServerProto<T> for LineProto {
    /// For this protocol style, `Request` matches the codec `In` type
    type Request = String;

    /// For this protocol style, `Response` matches the coded `Out` type
    type Response = String;

    /// A bit of boilerplate to hook in the codec:
    type Transport = Framed<T, LineCodec>;
    type BindTransport = Result<Self::Transport, io::Error>;
    fn bind_transport(&self, io: T) -> Self::BindTransport {
        Ok(io.framed(LineCodec))
    }
}

use tokio_service::Service;
pub struct Echo;
use futures::{future, Future, BoxFuture};
impl Service for Echo {
    // These types must match the corresponding protocol types:
    type Request = String;
    type Response = String;

    // For non-streaming protocols, service errors are always io::Error
    type Error = io::Error;

    // The future for computing the response; box it for simplicity.
    type Future = BoxFuture<Self::Response, Self::Error>;

    // Produce a future for computing a response from a request.
    fn call(&self, req: Self::Request) -> Self::Future {
        // In this case, the response is immediate.
        future::ok(req).boxed()
    }
}
use tokio_proto::TcpServer;

fn main() {
    // Specify the localhost address
    let addr = "0.0.0.0:1337".parse().unwrap();

    // The builder requires a protocol and an address
    let server = TcpServer::new(LineProto, addr);

    // We provide a way to *instantiate* the service for each new
    // connection; here, we just immediately return a new instance.
    server.serve(|| Ok(Echo));
}
<commit_msg>Use non-proto version<commit_after>//! An "hello world" echo server with tokio-core
//!
//! This server will create a TCP listener, accept connections in a loop, and
//! simply write back everything that's read off of each TCP connection. Each
//! TCP connection is processed concurrently with all other TCP connections, and
//! each connection will have its own buffer that it's reading in/out of.
//!
//! To see this server in action, you can run this in one terminal:
//!
//!     cargo run --example echo
//!
//! and in another terminal you can run:
//!
//!     cargo run --example connect 127.0.0.1:8080
//!
//! Each line you type in to the `connect` terminal should be echo'd back to
//! you! If you open up multiple terminals running the `connect` example you
//! should be able to see them all make progress simultaneously.

extern crate futures;
extern crate tokio_core;

use std::env;
use std::net::SocketAddr;

use futures::Future;
use futures::stream::Stream;
use tokio_core::io::{copy, Io};
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;

fn main() {
    // Allow passing an address to listen on as the first argument of this
    // program, but otherwise we'll just set up our TCP listener on
    // 127.0.0.1:8080 for connections.
    let addr = env::args().nth(1).unwrap_or("127.0.0.1:1337".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    // First up we'll create the event loop that's going to drive this server.
    // This is done by creating an instance of the `Core` type, tokio-core's
    // event loop. Most functions in tokio-core return an `io::Result`, and
    // `Core::new` is no exception. For this example, though, we're mostly just
    // ignoring errors, so we unwrap the return value.
    //
    // After the event loop is created we acquire a handle to it through the
    // `handle` method. With this handle we'll then later be able to create I/O
    // objects and spawn futures.
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    // Next up we create a TCP listener which will listen for incoming
    // connections. This TCP listener is bound to the address we determined
    // above and must be associated with an event loop, so we pass in a handle
    // to our event loop. After the socket's created we inform that we're ready
    // to go and start accepting connections.
    let socket = TcpListener::bind(&addr, &handle).unwrap();
    println!("Listening on: {}", addr);

    // Here we convert the `TcpListener` to a stream of incoming connections
    // with the `incoming` method. We then define how to process each element in
    // the stream with the `for_each` method.
    //
    // This combinator, defined on the `Stream` trait, will allow us to define a
    // computation to happen for all items on the stream (in this case TCP
    // connections made to the server).  The return value of the `for_each`
    // method is itself a future representing processing the entire stream of
    // connections, and ends up being our server.
    let done = socket.incoming().for_each(move |(socket, addr)| {

        // Once we're inside this closure this represents an accepted client
        // from our server. The `socket` is the client connection and `addr` is
        // the remote address of the client (similar to how the standard library
        // operates).
        //
        // We just want to copy all data read from the socket back onto the
        // socket itself (e.g. "echo"). We can use the standard `io::copy`
        // combinator in the `tokio-core` crate to do precisely this!
        //
        // The `copy` function takes two arguments, where to read from and where
        // to write to. We only have one argument, though, with `socket`.
        // Luckily there's a method, `Io::split`, which will split an Read/Write
        // stream into its two halves. This operation allows us to work with
        // each stream independently, such as pass them as two arguments to the
        // `copy` function.
        //
        // The `copy` function then returns a future, and this future will be
        // resolved when the copying operation is complete, resolving to the
        // amount of data that was copied.
        let (reader, writer) = socket.split();
        let amt = copy(reader, writer);

        // After our copy operation is complete we just print out some helpful
        // information.
        let msg = amt.then(move |result| {
            match result {
                Ok(amt) => println!("wrote {} bytes to {}", amt, addr),
                Err(e) => println!("error on {}: {}", addr, e),
            }

            Ok(())
        });

        // And this is where much of the magic of this server happens. We
        // crucially want all clients to make progress concurrently, rather than
        // blocking one on completion of another. To achieve this we use the
        // `spawn` function on `Handle` to essentially execute some work in the
        // background.
        //
        // This function will transfer ownership of the future (`msg` in this
        // case) to the event loop that `handle` points to. The event loop will
        // then drive the future to completion.
        //
        // Essentially here we're spawning a new task to run concurrently, which
        // will allow all of our clients to be processed concurrently.
        handle.spawn(msg);

        Ok(())
    });

    // And finally now that we've define what our server is, we run it! We
    // didn't actually do much I/O up to this point and this `Core::run` method
    // is responsible for driving the entire server to completion.
    //
    // The `run` method will return the result of the future that it's running,
    // but in our case the `done` future won't ever finish because a TCP
    // listener is never done accepting clients. That basically just means that
    // we're going to be running the server until it's killed (e.g. ctrl-c).
    core.run(done).unwrap();
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate openssl;
extern crate indy_crypto;

use errors::common::CommonError;

use services::anoncreds::constants::LARGE_MVECT;
use utils::crypto::bn::BigNumber;
use std::hash::Hash;
use std::cmp::max;
use std::collections::HashMap;
use self::indy_crypto::pair::{GroupOrderElement, PointG1, Pair};

#[cfg(not(test))]
pub fn random_qr(n: &BigNumber) -> Result<BigNumber, CommonError> {
    let random = n
        .rand_range()?
        .sqr(None)?
        .modulus(&n, None)?;
    Ok(random)
}

#[cfg(test)]
pub fn random_qr(n: &BigNumber) -> Result<BigNumber, CommonError> {
    Ok(BigNumber::from_dec("64684820421150545443421261645532741305438158267230326415141505826951816460650437611148133267480407958360035501128469885271549378871140475869904030424615175830170939416512594291641188403335834762737251794282186335118831803135149622404791467775422384378569231649224208728902565541796896860352464500717052768431523703881746487372385032277847026560711719065512366600220045978358915680277126661923892187090579302197390903902744925313826817940566429968987709582805451008234648959429651259809188953915675063700676546393568304468609062443048457324721450190021552656280473128156273976008799243162970386898307404395608179975243")?)
}

pub fn bitwise_or_big_int(a: &BigNumber, b: &BigNumber) -> Result<BigNumber, CommonError> {
    let significant_bits = max(a.num_bits()?, b.num_bits()?);
    let mut result = BigNumber::new()?;
    for i in 0..significant_bits {
        if a.is_bit_set(i)? || b.is_bit_set(i)? {
            result.set_bit(i)?;
        }
    }
    Ok(result)
}

//Byte order: Little
pub fn transform_u32_to_array_of_u8(x: u32) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();
    for i in (0..4).rev() {
        result.push((x >> i * 8) as u8);
    }
    result
}

pub fn get_hash_as_int(nums: &mut Vec<Vec<u8>>) -> Result<BigNumber, CommonError> {
    nums.sort();

    let mut hashed_array: Vec<u8> = BigNumber::hash_array(&nums)?;
    hashed_array.reverse();

    BigNumber::from_bytes(&hashed_array[..])
}

pub fn get_mtilde(unrevealed_attrs: &Vec<String>)
                  -> Result<HashMap<String, BigNumber>, CommonError> {
    let mut mtilde: HashMap<String, BigNumber> = HashMap::new();

    for attr in unrevealed_attrs.iter() {
        mtilde.insert(attr.clone(), BigNumber::rand(LARGE_MVECT)?);
    }
    Ok(mtilde)
}

fn largest_square_less_than(delta: usize) -> usize {
    (delta as f64).sqrt().floor() as usize
}

pub fn four_squares(delta: i32) -> Result<HashMap<String, BigNumber>, CommonError> {
    if delta < 0 {
        return Err(CommonError::InvalidStructure(format!("Cannot get the four squares for delta {} ", delta)));
    }

    let mut roots: [usize; 5] = [0; 5];
    let mut total: [usize; 5] = [0; 5];
    let mut tried: Vec<String> = Vec::new();

    roots[0] = largest_square_less_than(delta as usize);
    total[0] = delta as usize;

    let mut i = 1;

    while i <= 4 {
        total[i] = total[i - 1] - roots[i - 1].pow(2);

        if total[i] == 0 {
            break
        } else if i == 4 {
            i = adjust_index(&mut roots, i, &mut tried);
            continue
        }

        roots[i] = largest_square_less_than(total[i]);
        i += 1;
    }

    let mut res: HashMap<String, BigNumber> = HashMap::new();
    res.insert("0".to_string(), BigNumber::from_dec(&roots[0].to_string()[..])?);
    res.insert("1".to_string(), BigNumber::from_dec(&roots[1].to_string()[..])?);
    res.insert("2".to_string(), BigNumber::from_dec(&roots[2].to_string()[..])?);
    res.insert("3".to_string(), BigNumber::from_dec(&roots[3].to_string()[..])?);

    Ok(res)
}

fn adjust_index(roots: &mut [usize; 5], i: usize, tried: &mut Vec<String>) -> usize {
    let mut index = i;
    let mut k: String = gen_key(roots);

    tried.push(k.clone());
    while tried.contains(&k) {
        roots[index - 1] -= 1;
        while roots[index - 1] == 0 {
            index -= 1;
            roots[index - 1] -= 1;
        }
        k = gen_key(roots);
    }
    return index;
}

fn gen_key(roots: &mut [usize; 5]) -> String {
    roots.clone().sort();
    format!("{:?}", roots)
}

pub trait BytesView {
    fn to_bytes(&self) -> Result<Vec<u8>, CommonError>;
}

impl BytesView for PointG1 {
    fn to_bytes(&self) -> Result<Vec<u8>, CommonError> {
        Ok(self.to_bytes()?)
    }
}

impl BytesView for GroupOrderElement {
    fn to_bytes(&self) -> Result<Vec<u8>, CommonError> {
        Ok(self.to_bytes()?)
    }
}

impl BytesView for Pair {
    fn to_bytes(&self) -> Result<Vec<u8>, CommonError> {
        Ok(self.to_bytes()?)
    }
}

pub trait AppendByteArray {
    fn append_vec<T: BytesView>(&mut self, other: &Vec<T>) -> Result<(), CommonError>;
}

impl AppendByteArray for Vec<Vec<u8>> {
    fn append_vec<T: BytesView>(&mut self, other: &Vec<T>) -> Result<(), CommonError> {
        for el in other.iter() {
            self.push(el.to_bytes()?);
        }
        Ok(())
    }
}

pub fn clone_bignum_map<K: Clone + Eq + Hash>(other: &HashMap<K, BigNumber>)
                                              -> Result<HashMap<K, BigNumber>, CommonError> {
    let mut res: HashMap<K, BigNumber> = HashMap::new();
    for (k, v) in other {
        res.insert(k.clone(), v.clone()?);
    }
    Ok(res)
}

pub fn group_element_to_bignum(el: &GroupOrderElement) -> Result<BigNumber, CommonError> {
    Ok(BigNumber::from_bytes(&el.to_bytes()?)?)
}

pub fn bignum_to_group_element(num: &BigNumber) -> Result<GroupOrderElement, CommonError> {
    Ok(GroupOrderElement::from_bytes(&num.to_bytes()?)?)
}

pub fn get_composite_id(issuer_did: &str, schema_seq_no: i32) -> String {
    issuer_did.to_string() + ":" + &schema_seq_no.to_string()
}

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

    #[test]
    fn bitwise_or_big_int_works() {
        let a = BigNumber::from_dec("778378032744961463933002553964902776831187587689736807008034459507677878432383414623740074");
        let b = BigNumber::from_dec("1018517988167243043134222844204689080525734196832968125318070224677190649881668353091698688");
        let result = BigNumber::from_dec("1796896020912204507067225398169591857356921784522704932326104684184868528314051767715438762");
        assert_eq!(result.unwrap(), bitwise_or_big_int(&a.unwrap(), &b.unwrap()).unwrap());
    }

    #[test]
    fn get_hash_as_int_works() {
        let mut nums = vec![
            BigNumber::from_hex("ff9d2eedfee9cffd9ef6dbffedff3fcbef4caecb9bffe79bfa94d3fdf6abfbff").unwrap().to_bytes().unwrap(),
            BigNumber::from_hex("ff9d2eedfee9cffd9ef6dbffedff3fcbef4caecb9bffe79bfa9168615ccbc546").unwrap().to_bytes().unwrap()
        ];
        let res = get_hash_as_int(&mut nums);

        assert!(res.is_ok());
        assert_eq!("9E2A0653691B96A9B55B3D1133F9FEE2F2C37B848DBADF2F70DFFFE9E47C5A5D", res.unwrap().to_hex().unwrap());
    }

    #[test]
    fn four_squares_works() {
        let res = four_squares(10 as i32);
        let res_data = res.unwrap();

        assert_eq!("3".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("1".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("3").unwrap().to_dec().unwrap());

        let res = four_squares(107 as i32);
        let res_data = res.unwrap();

        assert_eq!("9".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("5".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("1".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("3").unwrap().to_dec().unwrap());

        let res = four_squares(112 as i32);
        let res_data = res.unwrap();

        assert_eq!("10".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("3").unwrap().to_dec().unwrap());

        let res = four_squares(253 as i32);
        let res_data = res.unwrap();

        assert_eq!("14".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("7".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("3").unwrap().to_dec().unwrap());

        let res = four_squares(0 as i32);
        let res_data = res.unwrap();

        assert_eq!("0".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("3").unwrap().to_dec().unwrap());

        let res = four_squares(25 as i32);
        let res_data = res.unwrap();

        assert_eq!("5".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("3").unwrap().to_dec().unwrap());
    }

    #[test]
    fn transform_u32_to_array_of_u8_works() {
        let int = 0x74BA7445;
        let answer = vec![0x74, 0xBA, 0x74, 0x45];
        assert_eq!(transform_u32_to_array_of_u8(int), answer)
    }
}<commit_msg>Replaced algorithm for finding four squares <commit_after>extern crate rand;
extern crate openssl;
extern crate indy_crypto;

use errors::common::CommonError;

use services::anoncreds::constants::LARGE_MVECT;
use utils::crypto::bn::BigNumber;
use std::hash::Hash;
use std::cmp::max;
use std::collections::HashMap;
use self::indy_crypto::pair::{GroupOrderElement, PointG1, Pair};

#[cfg(not(test))]
pub fn random_qr(n: &BigNumber) -> Result<BigNumber, CommonError> {
    let random = n
        .rand_range()?
        .sqr(None)?
        .modulus(&n, None)?;
    Ok(random)
}

#[cfg(test)]
pub fn random_qr(n: &BigNumber) -> Result<BigNumber, CommonError> {
    Ok(BigNumber::from_dec("64684820421150545443421261645532741305438158267230326415141505826951816460650437611148133267480407958360035501128469885271549378871140475869904030424615175830170939416512594291641188403335834762737251794282186335118831803135149622404791467775422384378569231649224208728902565541796896860352464500717052768431523703881746487372385032277847026560711719065512366600220045978358915680277126661923892187090579302197390903902744925313826817940566429968987709582805451008234648959429651259809188953915675063700676546393568304468609062443048457324721450190021552656280473128156273976008799243162970386898307404395608179975243")?)
}

pub fn bitwise_or_big_int(a: &BigNumber, b: &BigNumber) -> Result<BigNumber, CommonError> {
    let significant_bits = max(a.num_bits()?, b.num_bits()?);
    let mut result = BigNumber::new()?;
    for i in 0..significant_bits {
        if a.is_bit_set(i)? || b.is_bit_set(i)? {
            result.set_bit(i)?;
        }
    }
    Ok(result)
}

//Byte order: Little
pub fn transform_u32_to_array_of_u8(x: u32) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();
    for i in (0..4).rev() {
        result.push((x >> i * 8) as u8);
    }
    result
}

pub fn get_hash_as_int(nums: &mut Vec<Vec<u8>>) -> Result<BigNumber, CommonError> {
    nums.sort();

    let mut hashed_array: Vec<u8> = BigNumber::hash_array(&nums)?;
    hashed_array.reverse();

    BigNumber::from_bytes(&hashed_array[..])
}

pub fn get_mtilde(unrevealed_attrs: &Vec<String>)
                  -> Result<HashMap<String, BigNumber>, CommonError> {
    let mut mtilde: HashMap<String, BigNumber> = HashMap::new();

    for attr in unrevealed_attrs.iter() {
        mtilde.insert(attr.clone(), BigNumber::rand(LARGE_MVECT)?);
    }
    Ok(mtilde)
}

fn largest_square_less_than(delta: usize) -> usize {
    (delta as f64).sqrt().floor() as usize
}

pub fn four_squares(delta: i32) -> Result<HashMap<String, BigNumber>, CommonError> {
    if delta < 0 {
        return Err(CommonError::InvalidStructure(format!("Cannot get the four squares for delta {} ", delta)));
    }

    let d = delta as usize;
    let mut roots: [usize; 4] = [largest_square_less_than(d), 0, 0, 0];

    'outer: for i in (1 .. roots[0] + 1).rev() {
        roots[0] = i;
        if d == roots[0].pow(2) {
            roots[1] = 0;
            roots[2] = 0;
            roots[3] = 0;
            break 'outer;
        }
        roots[1] = largest_square_less_than(d - roots[0].pow(2));
        for j in (1 .. roots[1] + 1).rev() {
            roots[1] = j;
            if d == roots[0].pow(2) + roots[1].pow(2) {
                roots[2] = 0;
                roots[3] = 0;
                break 'outer;
            }
            roots[2] = largest_square_less_than(d - roots[0].pow(2) - roots[1].pow(2));
            for k in (1 .. roots[2] + 1).rev() {
                roots[2] = k;
                if d == roots[0].pow(2) + roots[1].pow(2) + roots[2].pow(2) {
                    roots[3] = 0;
                    break 'outer;
                }
                roots[3] = largest_square_less_than(d - roots[0].pow(2) - roots[1].pow(2) - roots[2].pow(2));
                if d == roots[0].pow(2) + roots[1].pow(2) + roots[2].pow(2) + roots[3].pow(2) {
                    break 'outer;
                }
            }
        }
    }

    let mut res: HashMap<String, BigNumber> = HashMap::new();
    res.insert("0".to_string(), BigNumber::from_dec(&roots[0].to_string()[..])?);
    res.insert("1".to_string(), BigNumber::from_dec(&roots[1].to_string()[..])?);
    res.insert("2".to_string(), BigNumber::from_dec(&roots[2].to_string()[..])?);
    res.insert("3".to_string(), BigNumber::from_dec(&roots[3].to_string()[..])?);

    Ok(res)
}

pub trait BytesView {
    fn to_bytes(&self) -> Result<Vec<u8>, CommonError>;
}

impl BytesView for PointG1 {
    fn to_bytes(&self) -> Result<Vec<u8>, CommonError> {
        Ok(self.to_bytes()?)
    }
}

impl BytesView for GroupOrderElement {
    fn to_bytes(&self) -> Result<Vec<u8>, CommonError> {
        Ok(self.to_bytes()?)
    }
}

impl BytesView for Pair {
    fn to_bytes(&self) -> Result<Vec<u8>, CommonError> {
        Ok(self.to_bytes()?)
    }
}

pub trait AppendByteArray {
    fn append_vec<T: BytesView>(&mut self, other: &Vec<T>) -> Result<(), CommonError>;
}

impl AppendByteArray for Vec<Vec<u8>> {
    fn append_vec<T: BytesView>(&mut self, other: &Vec<T>) -> Result<(), CommonError> {
        for el in other.iter() {
            self.push(el.to_bytes()?);
        }
        Ok(())
    }
}

pub fn clone_bignum_map<K: Clone + Eq + Hash>(other: &HashMap<K, BigNumber>)
                                              -> Result<HashMap<K, BigNumber>, CommonError> {
    let mut res: HashMap<K, BigNumber> = HashMap::new();
    for (k, v) in other {
        res.insert(k.clone(), v.clone()?);
    }
    Ok(res)
}

pub fn group_element_to_bignum(el: &GroupOrderElement) -> Result<BigNumber, CommonError> {
    Ok(BigNumber::from_bytes(&el.to_bytes()?)?)
}

pub fn bignum_to_group_element(num: &BigNumber) -> Result<GroupOrderElement, CommonError> {
    Ok(GroupOrderElement::from_bytes(&num.to_bytes()?)?)
}

pub fn get_composite_id(issuer_did: &str, schema_seq_no: i32) -> String {
    issuer_did.to_string() + ":" + &schema_seq_no.to_string()
}

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

    #[test]
    fn bitwise_or_big_int_works() {
        let a = BigNumber::from_dec("778378032744961463933002553964902776831187587689736807008034459507677878432383414623740074");
        let b = BigNumber::from_dec("1018517988167243043134222844204689080525734196832968125318070224677190649881668353091698688");
        let result = BigNumber::from_dec("1796896020912204507067225398169591857356921784522704932326104684184868528314051767715438762");
        assert_eq!(result.unwrap(), bitwise_or_big_int(&a.unwrap(), &b.unwrap()).unwrap());
    }

    #[test]
    fn get_hash_as_int_works() {
        let mut nums = vec![
            BigNumber::from_hex("ff9d2eedfee9cffd9ef6dbffedff3fcbef4caecb9bffe79bfa94d3fdf6abfbff").unwrap().to_bytes().unwrap(),
            BigNumber::from_hex("ff9d2eedfee9cffd9ef6dbffedff3fcbef4caecb9bffe79bfa9168615ccbc546").unwrap().to_bytes().unwrap()
        ];
        let res = get_hash_as_int(&mut nums);

        assert!(res.is_ok());
        assert_eq!("9E2A0653691B96A9B55B3D1133F9FEE2F2C37B848DBADF2F70DFFFE9E47C5A5D", res.unwrap().to_hex().unwrap());
    }

    #[test]
    fn four_squares_works() {
        let res = four_squares(107 as i32);
        let res_data = res.unwrap();

        assert_eq!("9".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("5".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("1".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("0".to_string(), res_data.get("3").unwrap().to_dec().unwrap());

        let res = four_squares(112 as i32);
        let res_data = res.unwrap();

        assert_eq!("10".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("3").unwrap().to_dec().unwrap());

        let res = four_squares(253 as i32);
        let res_data = res.unwrap();

        assert_eq!("14".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("7".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("2".to_string(), res_data.get("3").unwrap().to_dec().unwrap());

        let res = four_squares(1506099439 as i32);
        let res_data = res.unwrap();

        assert_eq!("38807".to_string(), res_data.get("0").unwrap().to_dec().unwrap());
        assert_eq!("337".to_string(), res_data.get("1").unwrap().to_dec().unwrap());
        assert_eq!("50".to_string(), res_data.get("2").unwrap().to_dec().unwrap());
        assert_eq!("11".to_string(), res_data.get("3").unwrap().to_dec().unwrap());
    }

    #[test]
    fn transform_u32_to_array_of_u8_works() {
        let int = 0x74BA7445;
        let answer = vec![0x74, 0xBA, 0x74, 0x45];
        assert_eq!(transform_u32_to_array_of_u8(int), answer)
    }
}<|endoftext|>