<commit_before>//! Taken from http://www.itu.int/rec/T-REC-X.680-200811-I

#![allow(dead_code)] // Until parser is complete. Too noisy otherwise

#[macro_use]
extern crate nom;

extern crate regex;

use std::str::FromStr;
use nom::{IResult, Needed};

/// Returns `true` if the character is a newline character according to ASN.1.
///
/// Defined in X.680 §12.1.6
fn is_newline(ch: char) -> bool {
    match ch {
        // LINE FEED
        '\n' => true,

        // VERTICAL TABULATION
        '\x0B' => true,

        // FORM FEED
        '\x0C' => true,

        // CARRIAGE RETURN
        '\x0D' => true,
        _ => false,
    }
}

named!(newline<&str, &str>,
       alt!(
           tag_s!("\n") |
           tag_s!("\x0B") |
           tag_s!("\x0C") |
           tag_s!("\x0D")
       )
);

/// Returns `true` if the character is a whitespace character according to ASN.1.
///
/// Defined in X.680 §12.1.6
fn is_whitespace(ch: char) -> bool {
    match ch {
        ' ' | '\t' => true,

        // All newline characters are also whitespace
        _ if is_newline(ch) => true,
        _ => false,
    }
}

/// Takes input until it finds the end of a single-line comment. This will not consume the ending
/// character(s) of the comment.
fn take_until_single_line_comment_end(input: &str) -> IResult<&str, &str> {
    let mut offset = input.len();
    let mut chars = input.char_indices().peekable();
    loop {
        match chars.next() {
            Some((i, c)) => {
                match c {
                    '-' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '-' {
                                    offset = i;
                                    break;
                                } else {
                                    // Safe to advance because we know that the peeked char is not
                                    // a dash.
                                    chars.next();
                                    continue;
                                }
                            },
                            // Got a single "-" at the end of the string. Consider it to be part of
                            // the comment.
                            None => {
                                // Normally, adding 1 to a byte offset in a `&str` could lead to an
                                // invalid access of a non-character boundary. In this case, we
                                // know that the last character was a "-", which is a single byte.
                                offset = i + 1;
                                break
                            }
                        }
                    },
                    _ if is_newline(c) => {
                        offset = i;
                        break
                    },
                    _ => continue,
                }
            },
            None => break,
        }
    }

    if offset < input.len() {
        IResult::Done(&input[offset..], &input[..offset])
    } else {
        IResult::Done("", input)
    }
}

/// Parse a single-line comment.
///
/// Defined in X.680 §12.6a
named!(single_line_comment<&str, &str>,
    delimited!(
        tag_s!("--"),
        take_until_single_line_comment_end,
        alt!(newline | tag_s!("--"))
    )
);

fn offset<T: AsRef<[u8]>>(base: T, sub: T) -> usize {
    let fst = base.as_ref().as_ptr();
    let snd = sub.as_ref().as_ptr();

    snd as usize - fst as usize
}

/// Version of `recognize!` macro that can be used for strings.
macro_rules! recognize_s (
  ($i:expr, $submac:ident!( $($args:tt)* )) => (
    {
      use ::nom::IResult;
      match $submac!($i, $($args)*) {
        IResult::Done(i,_) => {
          let index = offset($i, i);
          println!("dollar input: '{:?}', index: {}", $i, index);
          IResult::Done(i, &($i)[..index])
        },
        IResult::Error(e)      => return IResult::Error(e),
        IResult::Incomplete(i) => return IResult::Incomplete(i)
      }
    }
  );
  ($i:expr, $f:expr) => (
    recognize_s!($i, call!($f))
  );
);

/// Parse a multi-line comment. Multi-line comments in ASN.1 can be nested arbitrarily, so the
/// following is a valid comment:
///
/// ```notrust
/// /* Outer
///  * /* Inner */
///  * Still a comment
///  */
/// ```
///
/// Multi-line comments must be closed. If the end of the file is reached before the closing tag is
/// found, this is an error.
///
/// Defined in X.680 §12.6b
fn multi_line_comment(input: &str) -> IResult<&str, &str> {
    let mut offset = input.len();

    let (inside_comment, _) = try_parse!(input, tag_s!("/*"));
    let mut chars = inside_comment.char_indices().peekable();

    // The previous `try_parse!` has already consumed an opening tag, so the beginning nesting
    // level is 1.
    let mut nest_level: u32 = 1;
    loop {
        let next_char = chars.next();
        println!("next_char: '{:?}'", next_char);
        match next_char {
            Some((i, c)) => {
                match c {
                    '/' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '*' {
                                    nest_level += 1;
                                } else {
                                    continue;
                                }
                            },
                            // Got a single "/" at the end of the string. This is a parse error, as
                            // all multi-line comments must be closed.
                            None => {
                                return IResult::Incomplete(Needed::Unknown);
                            }
                        }
                    },
                    '*' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '/' {
                                    // About to close the comment.
                                    if nest_level == 1 {
                                        offset = i;
                                        break;
                                    }
                                    nest_level -= 1;
                                } else {
                                    continue;
                                }
                            },
                            // Got a single "*" at the end of the string. This is a parse error, as
                            // all multi-line comments must be closed.
                            None => {
                                return IResult::Incomplete(Needed::Size(1));
                            }
                        }
                    }
                    _ => continue,
                }
            },
            None => break,
        }
    }

    println!("inside_comment: '{:?}', offset: {:?}", inside_comment, offset);
    if offset + 2 <= inside_comment.len() {
        // Consume the closing comment characters
        IResult::Done(&inside_comment[(offset + 2)..], &inside_comment[..offset])
    } else {
        IResult::Done("", inside_comment)
    }
}

/// Parse a comment.
///
/// Defined in X.680 §12.6
named!(comment<&str, &str>,
    alt!(
        single_line_comment |
        multi_line_comment
    )
);

/// Parse an integer.
///
/// Defined in X.680 §12.8
named!(number<&str, u64>,
    map_res!(
        re_find!(r"^(0|[1-9]\d*)"),
        FromStr::from_str
    )
);

/// Parse an `f64`.
///
/// Defined in X.680 §12.9
named!(realnumber<&str, f64>,
   map_res!(
       re_find!(r"^\d+(\.\d*)?([eE]-?(0|[1-9]\d*))?"),
       FromStr::from_str
   )
);

#[cfg(test)]
mod tests {

    use super::{number, realnumber, single_line_comment, take_until_single_line_comment_end,
                newline, multi_line_comment, comment};
    use nom::IResult::{Done, Incomplete};
    use nom::{IResult, Needed};

    /// Simple way of specifying an `IResult::Done` with no remaining input.
    macro_rules! done_result (
        ($res:expr) => (
            IResult::Done("", $res)
        )
    );

    #[test]
    fn test_newline() {
        assert_eq!(newline("\n"), done_result!("\n"));
        assert_eq!(newline("\x0B"), done_result!("\x0B"));
    }

    #[test]
    fn test_take_until_single_line_comment_end() {
        assert_eq!(take_until_single_line_comment_end(" thing\nmore"), Done("\nmore", " thing"));
        assert_eq!(take_until_single_line_comment_end(" thing\n"), Done("\n", " thing"));
        assert_eq!(take_until_single_line_comment_end(" thing--"), Done("--", " thing"));
        assert_eq!(take_until_single_line_comment_end(" dash-in-middle--"),
                   Done("--", " dash-in-middle"));
        assert_eq!(take_until_single_line_comment_end(" dash-in-middle\x0C"),
                   Done("\x0C", " dash-in-middle"));

        // Empty comments
        assert_eq!(take_until_single_line_comment_end("\n"), Done("\n", ""));
        assert_eq!(take_until_single_line_comment_end("--"), Done("--", ""));

        assert_eq!(take_until_single_line_comment_end("stuff-"), done_result!("stuff-"));
        assert_eq!(take_until_single_line_comment_end("-"), done_result!("-"));
    }

    fn single_line_comment_tests<F>(parser: F)
        where F : Fn(&str) -> IResult<&str, &str> {

        assert_eq!(parser("-- thing\nmore"), Done("more", " thing"));
        assert_eq!(parser("-- thing--"), done_result!(" thing"));
        assert_eq!(parser("-- thing\n"), done_result!(" thing"));
        assert_eq!(parser("-- dash-in-middle--"), done_result!(" dash-in-middle"));
        assert_eq!(parser("-- dash-in-middle\x0C"), done_result!(" dash-in-middle"));

        // "/*" and "*/ have no special meaning in single-line comment.
        assert_eq!(single_line_comment("--/* still\nsingle"), Done("single", "/* still"));
        assert_eq!(single_line_comment("--*/* still\nsingle"), Done("single", "*/* still"));
        assert_eq!(single_line_comment("--*/ *//* still\nsingle"),
                   Done("single", "*/ *//* still"));

        assert_eq!(single_line_comment("--\n"), done_result!(""));
        assert_eq!(single_line_comment("----"), done_result!(""));
    }

    #[test]
    fn test_single_line_comment() {
        single_line_comment_tests(single_line_comment);
    }

    fn multi_line_comment_tests<F>(parser: F)
        where F : Fn(&str) -> IResult<&str, &str> {

        assert_eq!(parser("/* stuff */"), done_result!(" stuff "));
        assert_eq!(parser("/* line1\nline2 */"), done_result!(" line1\nline2 "));
        assert_eq!(parser("/**/"), done_result!(""));

        // "--" in multi-line comment has no special meaning.
        assert_eq!(parser("/* -- */"), done_result!(" -- "));

        // Incomplete
        assert_eq!(parser("/*/"), Incomplete(Needed::Unknown));
        assert_eq!(parser("/**"), Incomplete(Needed::Size(1)));

        // Nested comments
        assert_eq!(parser("/* Outer /* Inner */ Outer again */"),
                   done_result!(" Outer /* Inner */ Outer again "));
        assert_eq!(parser("/* Out /* In */ Out2 /* In2 */ Out3 */"),
                   done_result!(" Out /* In */ Out2 /* In2 */ Out3 "));
        assert_eq!(parser("/* Out /* In */ Out */ Extra */"),
                   Done(" Extra */", " Out /* In */ Out "));
    }

    #[test]
    fn test_multi_line_comment() {
        multi_line_comment_tests(multi_line_comment);
    }

    #[test]
    fn test_comment() {
        single_line_comment_tests(comment);
        multi_line_comment_tests(comment);
    }

    #[test]
    fn test_number() {
        assert_eq!(number("0"), done_result!(0));
        assert_eq!(number("1"), done_result!(1));

        // Matches the first zero, but considers the second digit to be leftover input.
        assert_eq!(number("01"), Done("1", 0));
    }

    #[test]
    fn test_realnumber() {
        assert_eq!(realnumber("1"), done_result!(1.0));
        assert_eq!(realnumber("0"), done_result!(0.0));

        // Can have a leading zero
        assert_eq!(realnumber("00"), done_result!(0.0));
        assert_eq!(realnumber("01"), done_result!(1.0));

        // Can have a trailing period without a factional part
        assert_eq!(realnumber("1."), done_result!(1.0));
        assert_eq!(realnumber("1.0"), done_result!(1.0));
        assert_eq!(realnumber("1.001"), done_result!(1.001));

        // Exponents
        assert_eq!(realnumber("1.1e3"), done_result!(1100.0));
        assert_eq!(realnumber("1.1e-3"), done_result!(0.0011));
        assert_eq!(realnumber("1.e3"), done_result!(1000.0));
        assert_eq!(realnumber("1e3"), done_result!(1000.0));
        assert_eq!(realnumber("1.1E3"), done_result!(1100.0));
        assert_eq!(realnumber("1.1E-3"), done_result!(0.0011));

        // Leading zero in exponent
        assert_eq!(realnumber("1.1E-03"), Done("3", 1.1));
        assert_eq!(realnumber("1.1E03"), Done("3", 1.1));
    }

}
<commit_msg>Some more documentation (to get Travis to run)<commit_after>//! An ASN.1 parser written in Rust.
//!
//! Taken from http://www.itu.int/rec/T-REC-X.680-200811-I

#![allow(dead_code)] // Until parser is complete. Too noisy otherwise

#[macro_use]
extern crate nom;

extern crate regex;

use std::str::FromStr;
use nom::{IResult, Needed};

/// Returns `true` if the character is a newline character according to ASN.1.
///
/// Defined in X.680 §12.1.6
fn is_newline(ch: char) -> bool {
    match ch {
        // LINE FEED
        '\n' => true,

        // VERTICAL TABULATION
        '\x0B' => true,

        // FORM FEED
        '\x0C' => true,

        // CARRIAGE RETURN
        '\x0D' => true,
        _ => false,
    }
}

named!(newline<&str, &str>,
       alt!(
           tag_s!("\n") |
           tag_s!("\x0B") |
           tag_s!("\x0C") |
           tag_s!("\x0D")
       )
);

/// Returns `true` if the character is a whitespace character according to ASN.1.
///
/// Defined in X.680 §12.1.6
fn is_whitespace(ch: char) -> bool {
    match ch {
        ' ' | '\t' => true,

        // All newline characters are also whitespace
        _ if is_newline(ch) => true,
        _ => false,
    }
}

/// Takes input until it finds the end of a single-line comment. This will not consume the ending
/// character(s) of the comment.
fn take_until_single_line_comment_end(input: &str) -> IResult<&str, &str> {
    let mut offset = input.len();
    let mut chars = input.char_indices().peekable();
    loop {
        match chars.next() {
            Some((i, c)) => {
                match c {
                    '-' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '-' {
                                    offset = i;
                                    break;
                                } else {
                                    // Safe to advance because we know that the peeked char is not
                                    // a dash.
                                    chars.next();
                                    continue;
                                }
                            },
                            // Got a single "-" at the end of the string. Consider it to be part of
                            // the comment.
                            None => {
                                // Normally, adding 1 to a byte offset in a `&str` could lead to an
                                // invalid access of a non-character boundary. In this case, we
                                // know that the last character was a "-", which is a single byte.
                                offset = i + 1;
                                break
                            }
                        }
                    },
                    _ if is_newline(c) => {
                        offset = i;
                        break
                    },
                    _ => continue,
                }
            },
            None => break,
        }
    }

    if offset < input.len() {
        IResult::Done(&input[offset..], &input[..offset])
    } else {
        IResult::Done("", input)
    }
}

/// Parse a single-line comment.
///
/// Defined in X.680 §12.6a
named!(single_line_comment<&str, &str>,
    delimited!(
        tag_s!("--"),
        take_until_single_line_comment_end,
        alt!(newline | tag_s!("--"))
    )
);

fn offset<T: AsRef<[u8]>>(base: T, sub: T) -> usize {
    let fst = base.as_ref().as_ptr();
    let snd = sub.as_ref().as_ptr();

    snd as usize - fst as usize
}

/// Version of `recognize!` macro that can be used for strings.
macro_rules! recognize_s (
  ($i:expr, $submac:ident!( $($args:tt)* )) => (
    {
      use ::nom::IResult;
      match $submac!($i, $($args)*) {
        IResult::Done(i,_) => {
          let index = offset($i, i);
          println!("dollar input: '{:?}', index: {}", $i, index);
          IResult::Done(i, &($i)[..index])
        },
        IResult::Error(e)      => return IResult::Error(e),
        IResult::Incomplete(i) => return IResult::Incomplete(i)
      }
    }
  );
  ($i:expr, $f:expr) => (
    recognize_s!($i, call!($f))
  );
);

/// Parse a multi-line comment. Multi-line comments in ASN.1 can be nested arbitrarily, so the
/// following is a valid comment:
///
/// ```notrust
/// /* Outer
///  * /* Inner */
///  * Still a comment
///  */
/// ```
///
/// Multi-line comments must be closed. If the end of the file is reached before the closing tag is
/// found, this is an error.
///
/// Defined in X.680 §12.6b
fn multi_line_comment(input: &str) -> IResult<&str, &str> {
    let mut offset = input.len();

    let (inside_comment, _) = try_parse!(input, tag_s!("/*"));
    let mut chars = inside_comment.char_indices().peekable();

    // The previous `try_parse!` has already consumed an opening tag, so the beginning nesting
    // level is 1.
    let mut nest_level: u32 = 1;
    loop {
        let next_char = chars.next();
        println!("next_char: '{:?}'", next_char);
        match next_char {
            Some((i, c)) => {
                match c {
                    '/' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '*' {
                                    nest_level += 1;
                                } else {
                                    continue;
                                }
                            },
                            // Got a single "/" at the end of the string. This is a parse error, as
                            // all multi-line comments must be closed.
                            None => {
                                return IResult::Incomplete(Needed::Unknown);
                            }
                        }
                    },
                    '*' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '/' {
                                    // About to close the comment.
                                    if nest_level == 1 {
                                        offset = i;
                                        break;
                                    }
                                    nest_level -= 1;
                                } else {
                                    continue;
                                }
                            },
                            // Got a single "*" at the end of the string. This is a parse error, as
                            // all multi-line comments must be closed.
                            None => {
                                return IResult::Incomplete(Needed::Size(1));
                            }
                        }
                    }
                    _ => continue,
                }
            },
            None => break,
        }
    }

    println!("inside_comment: '{:?}', offset: {:?}", inside_comment, offset);
    if offset + 2 <= inside_comment.len() {
        // Consume the closing comment characters
        IResult::Done(&inside_comment[(offset + 2)..], &inside_comment[..offset])
    } else {
        IResult::Done("", inside_comment)
    }
}

/// Parse a comment.
///
/// Defined in X.680 §12.6
named!(comment<&str, &str>,
    alt!(
        single_line_comment |
        multi_line_comment
    )
);

/// Parse an integer.
///
/// Defined in X.680 §12.8
named!(number<&str, u64>,
    map_res!(
        re_find!(r"^(0|[1-9]\d*)"),
        FromStr::from_str
    )
);

/// Parse an `f64`.
///
/// Defined in X.680 §12.9
named!(realnumber<&str, f64>,
   map_res!(
       re_find!(r"^\d+(\.\d*)?([eE]-?(0|[1-9]\d*))?"),
       FromStr::from_str
   )
);

#[cfg(test)]
mod tests {

    use super::{number, realnumber, single_line_comment, take_until_single_line_comment_end,
                newline, multi_line_comment, comment};
    use nom::IResult::{Done, Incomplete};
    use nom::{IResult, Needed};

    /// Simple way of specifying an `IResult::Done` with no remaining input.
    macro_rules! done_result (
        ($res:expr) => (
            IResult::Done("", $res)
        )
    );

    #[test]
    fn test_newline() {
        assert_eq!(newline("\n"), done_result!("\n"));
        assert_eq!(newline("\x0B"), done_result!("\x0B"));
    }

    #[test]
    fn test_take_until_single_line_comment_end() {
        assert_eq!(take_until_single_line_comment_end(" thing\nmore"), Done("\nmore", " thing"));
        assert_eq!(take_until_single_line_comment_end(" thing\n"), Done("\n", " thing"));
        assert_eq!(take_until_single_line_comment_end(" thing--"), Done("--", " thing"));
        assert_eq!(take_until_single_line_comment_end(" dash-in-middle--"),
                   Done("--", " dash-in-middle"));
        assert_eq!(take_until_single_line_comment_end(" dash-in-middle\x0C"),
                   Done("\x0C", " dash-in-middle"));

        // Empty comments
        assert_eq!(take_until_single_line_comment_end("\n"), Done("\n", ""));
        assert_eq!(take_until_single_line_comment_end("--"), Done("--", ""));

        assert_eq!(take_until_single_line_comment_end("stuff-"), done_result!("stuff-"));
        assert_eq!(take_until_single_line_comment_end("-"), done_result!("-"));
    }

    fn single_line_comment_tests<F>(parser: F)
        where F : Fn(&str) -> IResult<&str, &str> {

        assert_eq!(parser("-- thing\nmore"), Done("more", " thing"));
        assert_eq!(parser("-- thing--"), done_result!(" thing"));
        assert_eq!(parser("-- thing\n"), done_result!(" thing"));
        assert_eq!(parser("-- dash-in-middle--"), done_result!(" dash-in-middle"));
        assert_eq!(parser("-- dash-in-middle\x0C"), done_result!(" dash-in-middle"));

        // "/*" and "*/ have no special meaning in single-line comment.
        assert_eq!(single_line_comment("--/* still\nsingle"), Done("single", "/* still"));
        assert_eq!(single_line_comment("--*/* still\nsingle"), Done("single", "*/* still"));
        assert_eq!(single_line_comment("--*/ *//* still\nsingle"),
                   Done("single", "*/ *//* still"));

        assert_eq!(single_line_comment("--\n"), done_result!(""));
        assert_eq!(single_line_comment("----"), done_result!(""));
    }

    #[test]
    fn test_single_line_comment() {
        single_line_comment_tests(single_line_comment);
    }

    fn multi_line_comment_tests<F>(parser: F)
        where F : Fn(&str) -> IResult<&str, &str> {

        assert_eq!(parser("/* stuff */"), done_result!(" stuff "));
        assert_eq!(parser("/* line1\nline2 */"), done_result!(" line1\nline2 "));
        assert_eq!(parser("/**/"), done_result!(""));

        // "--" in multi-line comment has no special meaning.
        assert_eq!(parser("/* -- */"), done_result!(" -- "));

        // Incomplete
        assert_eq!(parser("/*/"), Incomplete(Needed::Unknown));
        assert_eq!(parser("/**"), Incomplete(Needed::Size(1)));

        // Nested comments
        assert_eq!(parser("/* Outer /* Inner */ Outer again */"),
                   done_result!(" Outer /* Inner */ Outer again "));
        assert_eq!(parser("/* Out /* In */ Out2 /* In2 */ Out3 */"),
                   done_result!(" Out /* In */ Out2 /* In2 */ Out3 "));
        assert_eq!(parser("/* Out /* In */ Out */ Extra */"),
                   Done(" Extra */", " Out /* In */ Out "));
    }

    #[test]
    fn test_multi_line_comment() {
        multi_line_comment_tests(multi_line_comment);
    }

    #[test]
    fn test_comment() {
        single_line_comment_tests(comment);
        multi_line_comment_tests(comment);
    }

    #[test]
    fn test_number() {
        assert_eq!(number("0"), done_result!(0));
        assert_eq!(number("1"), done_result!(1));

        // Matches the first zero, but considers the second digit to be leftover input.
        assert_eq!(number("01"), Done("1", 0));
    }

    #[test]
    fn test_realnumber() {
        assert_eq!(realnumber("1"), done_result!(1.0));
        assert_eq!(realnumber("0"), done_result!(0.0));

        // Can have a leading zero
        assert_eq!(realnumber("00"), done_result!(0.0));
        assert_eq!(realnumber("01"), done_result!(1.0));

        // Can have a trailing period without a factional part
        assert_eq!(realnumber("1."), done_result!(1.0));
        assert_eq!(realnumber("1.0"), done_result!(1.0));
        assert_eq!(realnumber("1.001"), done_result!(1.001));

        // Exponents
        assert_eq!(realnumber("1.1e3"), done_result!(1100.0));
        assert_eq!(realnumber("1.1e-3"), done_result!(0.0011));
        assert_eq!(realnumber("1.e3"), done_result!(1000.0));
        assert_eq!(realnumber("1e3"), done_result!(1000.0));
        assert_eq!(realnumber("1.1E3"), done_result!(1100.0));
        assert_eq!(realnumber("1.1E-3"), done_result!(0.0011));

        // Leading zero in exponent
        assert_eq!(realnumber("1.1E-03"), Done("3", 1.1));
        assert_eq!(realnumber("1.1E03"), Done("3", 1.1));
    }

}
<|endoftext|>


<commit_before>#![recursion_limit = "1024"]

#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate idna;

pub mod errors;

#[cfg(test)]
mod tests;

use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;

use errors::*;
use reqwest::IntoUrl;
use idna::domain_to_unicode;

#[derive(Debug)]
struct Suffix {
    rule: String,
    typ: Type,
}

#[derive(Debug)]
pub struct List {
    rules: HashMap<String, Vec<Suffix>>,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Type {
    Icann,
    Private,
}

#[derive(Debug, Clone)]
pub struct Domain {
    full: String,
    typ: Option<Type>,
    suffix: Option<String>,
    registrable: Option<String>,
}

const OFFICIAL_URL: &'static str = "https://publicsuffix.org/list/public_suffix_list.dat";

impl List {
    fn append(&mut self, rule: &str, typ: Type) -> Result<()> {
        rule.rsplit('.').next()
            .ok_or(ErrorKind::InvalidRule(rule.into()).into())
            .and_then(|tld| {
                if tld.is_empty() {
                    return Err(ErrorKind::InvalidRule(rule.into()).into());
                }
                Ok(tld)})
            .and_then(|tld| {
                self.rules.entry(tld.into()).or_insert(Vec::new())
                    .push(Suffix {
                        rule: rule.into(),
                        typ: typ,
                    });
                Ok(())
            })
    }

    fn build<R: Read>(mut reader: R) -> Result<List> {
        let mut res = String::new();
        reader.read_to_string(&mut res)?;

        let mut typ = Type::Icann;

        let mut list = List {
            rules: HashMap::new(),
        };

        for line in res.lines() {
            match line {
                line if line.contains("BEGIN ICANN DOMAINS") => { typ = Type::Icann; }
                line if line.contains("BEGIN PRIVATE DOMAINS") => { typ = Type::Private; }
                line if line.starts_with("//") => { continue; }
                line => {
                    let rule = match line.split_whitespace().next() {
                        Some(rule) => rule,
                        None => continue,
                    };
                    list.append(rule, typ)?;
                }
            }
        }

        Ok(list)
    }

    pub fn from_url<U: IntoUrl>(url: U) -> Result<List> {
        reqwest::get(url)
            .map_err(|err| ErrorKind::Request(err).into())
            .and_then(Self::build)
    }

    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<List> {
        File::open(path)
            .map_err(|err| ErrorKind::Io(err).into())
            .and_then(Self::build)
    }

    pub fn fetch() -> Result<List> {
        Self::from_url(OFFICIAL_URL)
    }

    fn find_type(&self, typ: Type) -> Vec<&String> {
        self.rules.values()
            .fold(Vec::new(), |mut res, ref suffices| {
                for suffix in *suffices {
                    if suffix.typ == typ {
                        res.push(&suffix.rule);
                    }
                }
                res
            })
    }

    pub fn icann(&self) -> Vec<&String> {
        self.find_type(Type::Icann)
    }

    pub fn private(&self) -> Vec<&String> {
        self.find_type(Type::Private)
    }

    pub fn all(&self) -> Vec<&String> {
        self.rules.values()
            .fold(Vec::new(), |mut res, ref suffices| {
                for suffix in *suffices {
                    res.push(&suffix.rule);
                }
                res
            })
    }

    pub fn parse_domain(&self, domain: &str) -> Result<Domain> {
        Domain::parse(domain, self)
    }
}

impl Domain {
    fn is_valid(domain: &str) -> bool {
        match domain {
            d if d.starts_with('.') => { return false; }
            _ => { true }
        }
    }

    fn find_possible_matches<'a>(domain: &str, list: &'a List) -> Result<Vec<&'a Suffix>> {
        let tld = match domain.rsplit('.').next() {
            Some(tld) => {
                if tld.is_empty() { return Ok(Vec::new()); }
                tld
            },
            None => { return Ok(Vec::new()); },
        };
        let candidates = match list.rules.get(tld) {
            Some(candidates) => candidates,
            None => { return Ok(Vec::new()); },
        };
        let candidates = candidates.iter()
            .fold(Vec::new(), |mut res, ref suffix| {
                res.push(*suffix);
                res
            });
        Ok(candidates)
    }

    fn assemble(input: &str, s_len: usize) -> String {
        let domain = input.to_lowercase();

        let d_labels: Vec<&str> = domain
            .trim_right_matches('.')
            .split('.').rev().collect();

        (&d_labels[..s_len]).iter().rev()
            .map(|part| *part)
            .collect::<Vec<_>>()
            .join(".")
    }

    fn find_match(input: &str, domain: &str, domain_is_valid: bool, candidates: Vec<&Suffix>) -> Result<Domain> {
        let d_labels: Vec<&str> = domain.split('.').rev().collect();

        let mut registrable = None;
        let mut suffix = None;
        let mut typ = None;
        let mut num_labels = 0;

        if domain_is_valid {
            let no_possible_matches_found = candidates.is_empty();

            for candidate in candidates {
                let s_labels: Vec<&str> = candidate.rule.split('.').rev().collect();
                if s_labels.len() > d_labels.len() { continue; }
                for (i, label) in s_labels.iter().enumerate() {
                    if *label == d_labels[i] || *label == "*" || label.trim_left_matches('!') == d_labels[i] {
                        if i == s_labels.len()-1 {
                            if s_labels.len() >= num_labels {
                                num_labels = s_labels.len();
                                typ = Some(candidate.typ);
                                let s_len = if label.starts_with("!") {
                                    s_labels.len()-1
                                } else {
                                    s_labels.len()
                                };
                                suffix = Some(Self::assemble(input, s_len));
                                if d_labels.len() > s_len {
                                    registrable = Some(Self::assemble(input, s_len+1));
                                } else {
                                    registrable = None;
                                }
                            }
                        }
                    } else {
                        break;
                    }
                }
            }

            if suffix.is_none() && d_labels.len() > 1 && no_possible_matches_found {
                    suffix = Some(Self::assemble(input, 1));
                    registrable = Some(Self::assemble(input, 2));
            }
        }

        Ok(Domain {
            full: input.to_string(),
            typ: typ,
            suffix: suffix,
            registrable: registrable,
        })
    }

    pub fn parse(domain: &str, list: &List) -> Result<Domain> {
        let input = domain;

        let domain = input.trim().trim_right_matches('.');
        let (domain, res) = domain_to_unicode(domain);
        if let Err(errors) = res {
            return Err(ErrorKind::Uts46(errors).into());
        }

        let domain_is_valid = Domain::is_valid(input);

        let res = if domain_is_valid {
            Self::find_possible_matches(&domain, list)?
        } else {
            Vec::new()
        };

        Self::find_match(input, &domain, domain_is_valid, res)
    }

    pub fn root_domain(&self) -> Option<&String> {
        match self.registrable {
            Some(ref registrable) => Some(registrable),
            None => None,
        }
    }

    pub fn suffix(&self) -> Option<&String> {
        match self.suffix {
            Some(ref suffix) => Some(suffix),
            None => None,
        }
    }

    pub fn is_private(&self) -> bool {
        match self.typ {
            Some(typ) => match typ {
                Type::Icann => false,
                Type::Private => true,
            },
            None => false,
        }
    }

    pub fn is_icann(&self) -> bool {
        match self.typ {
            Some(typ) => match typ {
                Type::Icann => true,
                Type::Private => false,
            },
            None => false,
        }
    }
}
<commit_msg>Add has_known_suffix method for validating domains<commit_after>#![recursion_limit = "1024"]

#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate idna;

pub mod errors;

#[cfg(test)]
mod tests;

use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;

use errors::*;
use reqwest::IntoUrl;
use idna::domain_to_unicode;

#[derive(Debug)]
struct Suffix {
    rule: String,
    typ: Type,
}

#[derive(Debug)]
pub struct List {
    rules: HashMap<String, Vec<Suffix>>,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Type {
    Icann,
    Private,
}

#[derive(Debug, Clone)]
pub struct Domain {
    full: String,
    typ: Option<Type>,
    suffix: Option<String>,
    registrable: Option<String>,
}

const OFFICIAL_URL: &'static str = "https://publicsuffix.org/list/public_suffix_list.dat";

impl List {
    fn append(&mut self, rule: &str, typ: Type) -> Result<()> {
        rule.rsplit('.').next()
            .ok_or(ErrorKind::InvalidRule(rule.into()).into())
            .and_then(|tld| {
                if tld.is_empty() {
                    return Err(ErrorKind::InvalidRule(rule.into()).into());
                }
                Ok(tld)})
            .and_then(|tld| {
                self.rules.entry(tld.into()).or_insert(Vec::new())
                    .push(Suffix {
                        rule: rule.into(),
                        typ: typ,
                    });
                Ok(())
            })
    }

    fn build<R: Read>(mut reader: R) -> Result<List> {
        let mut res = String::new();
        reader.read_to_string(&mut res)?;

        let mut typ = Type::Icann;

        let mut list = List {
            rules: HashMap::new(),
        };

        for line in res.lines() {
            match line {
                line if line.contains("BEGIN ICANN DOMAINS") => { typ = Type::Icann; }
                line if line.contains("BEGIN PRIVATE DOMAINS") => { typ = Type::Private; }
                line if line.starts_with("//") => { continue; }
                line => {
                    let rule = match line.split_whitespace().next() {
                        Some(rule) => rule,
                        None => continue,
                    };
                    list.append(rule, typ)?;
                }
            }
        }

        Ok(list)
    }

    pub fn from_url<U: IntoUrl>(url: U) -> Result<List> {
        reqwest::get(url)
            .map_err(|err| ErrorKind::Request(err).into())
            .and_then(Self::build)
    }

    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<List> {
        File::open(path)
            .map_err(|err| ErrorKind::Io(err).into())
            .and_then(Self::build)
    }

    pub fn fetch() -> Result<List> {
        Self::from_url(OFFICIAL_URL)
    }

    fn find_type(&self, typ: Type) -> Vec<&String> {
        self.rules.values()
            .fold(Vec::new(), |mut res, ref suffices| {
                for suffix in *suffices {
                    if suffix.typ == typ {
                        res.push(&suffix.rule);
                    }
                }
                res
            })
    }

    pub fn icann(&self) -> Vec<&String> {
        self.find_type(Type::Icann)
    }

    pub fn private(&self) -> Vec<&String> {
        self.find_type(Type::Private)
    }

    pub fn all(&self) -> Vec<&String> {
        self.rules.values()
            .fold(Vec::new(), |mut res, ref suffices| {
                for suffix in *suffices {
                    res.push(&suffix.rule);
                }
                res
            })
    }

    pub fn parse_domain(&self, domain: &str) -> Result<Domain> {
        Domain::parse(domain, self)
    }
}

impl Domain {
    fn is_valid(domain: &str) -> bool {
        match domain {
            d if d.starts_with('.') => { return false; }
            _ => { true }
        }
    }

    fn find_possible_matches<'a>(domain: &str, list: &'a List) -> Result<Vec<&'a Suffix>> {
        let tld = match domain.rsplit('.').next() {
            Some(tld) => {
                if tld.is_empty() { return Ok(Vec::new()); }
                tld
            },
            None => { return Ok(Vec::new()); },
        };
        let candidates = match list.rules.get(tld) {
            Some(candidates) => candidates,
            None => { return Ok(Vec::new()); },
        };
        let candidates = candidates.iter()
            .fold(Vec::new(), |mut res, ref suffix| {
                res.push(*suffix);
                res
            });
        Ok(candidates)
    }

    fn assemble(input: &str, s_len: usize) -> String {
        let domain = input.to_lowercase();

        let d_labels: Vec<&str> = domain
            .trim_right_matches('.')
            .split('.').rev().collect();

        (&d_labels[..s_len]).iter().rev()
            .map(|part| *part)
            .collect::<Vec<_>>()
            .join(".")
    }

    fn find_match(input: &str, domain: &str, domain_is_valid: bool, candidates: Vec<&Suffix>) -> Result<Domain> {
        let d_labels: Vec<&str> = domain.split('.').rev().collect();

        let mut registrable = None;
        let mut suffix = None;
        let mut typ = None;
        let mut num_labels = 0;

        if domain_is_valid {
            let no_possible_matches_found = candidates.is_empty();

            for candidate in candidates {
                let s_labels: Vec<&str> = candidate.rule.split('.').rev().collect();
                if s_labels.len() > d_labels.len() { continue; }
                for (i, label) in s_labels.iter().enumerate() {
                    if *label == d_labels[i] || *label == "*" || label.trim_left_matches('!') == d_labels[i] {
                        if i == s_labels.len()-1 {
                            if s_labels.len() >= num_labels {
                                num_labels = s_labels.len();
                                typ = Some(candidate.typ);
                                let s_len = if label.starts_with("!") {
                                    s_labels.len()-1
                                } else {
                                    s_labels.len()
                                };
                                suffix = Some(Self::assemble(input, s_len));
                                if d_labels.len() > s_len {
                                    registrable = Some(Self::assemble(input, s_len+1));
                                } else {
                                    registrable = None;
                                }
                            }
                        }
                    } else {
                        break;
                    }
                }
            }

            if suffix.is_none() && d_labels.len() > 1 && no_possible_matches_found {
                    suffix = Some(Self::assemble(input, 1));
                    registrable = Some(Self::assemble(input, 2));
            }
        }

        Ok(Domain {
            full: input.to_string(),
            typ: typ,
            suffix: suffix,
            registrable: registrable,
        })
    }

    pub fn parse(domain: &str, list: &List) -> Result<Domain> {
        let input = domain;

        let domain = input.trim().trim_right_matches('.');
        let (domain, res) = domain_to_unicode(domain);
        if let Err(errors) = res {
            return Err(ErrorKind::Uts46(errors).into());
        }

        let domain_is_valid = Domain::is_valid(input);

        let res = if domain_is_valid {
            Self::find_possible_matches(&domain, list)?
        } else {
            Vec::new()
        };

        Self::find_match(input, &domain, domain_is_valid, res)
    }

    pub fn root_domain(&self) -> Option<&String> {
        match self.registrable {
            Some(ref registrable) => Some(registrable),
            None => None,
        }
    }

    pub fn suffix(&self) -> Option<&String> {
        match self.suffix {
            Some(ref suffix) => Some(suffix),
            None => None,
        }
    }

    pub fn is_private(&self) -> bool {
        match self.typ {
            Some(typ) => match typ {
                Type::Icann => false,
                Type::Private => true,
            },
            None => false,
        }
    }

    pub fn is_icann(&self) -> bool {
        match self.typ {
            Some(typ) => match typ {
                Type::Icann => true,
                Type::Private => false,
            },
            None => false,
        }
    }

    pub fn has_known_suffix(&self) -> bool {
        self.typ.is_some()
    }
}
<|endoftext|>


<commit_before>#![feature(asm)]
#![feature(core_intrinsics)]
#![feature(linkage)]
#![feature(naked_functions)]
#![cfg_attr(not(test), no_std)]
#![no_builtins]
// TODO(rust-lang/rust#35021) uncomment when that PR lands
// #![feature(rustc_builtins)]

// We disable #[no_mangle] for tests so that we can verify the test results
// against the native compiler-rt implementations of the builtins.

// NOTE cfg(all(feature = "c", ..)) indicate that compiler-rt provides an arch optimized
// implementation of that intrinsic and we'll prefer to use that

// TODO(rust-lang/rust#37029) use e.g. checked_div(_).unwrap_or_else(|| abort())
macro_rules! udiv {
    ($a:expr, $b:expr) => {
        unsafe {
            let a = $a;
            let b = $b;

            if b == 0 {
                ::core::intrinsics::abort()
            } else {
                ::core::intrinsics::unchecked_div(a, b)
            }
        }
    }
}

macro_rules! sdiv {
    ($sty:ident, $a:expr, $b:expr) => {
        unsafe {
            let a = $a;
            let b = $b;

            if b == 0 || (b == -1 && a == $sty::min_value()) {
                ::core::intrinsics::abort()
            } else {
                ::core::intrinsics::unchecked_div(a, b)
            }
        }
    }
}

macro_rules! urem {
    ($a:expr, $b:expr) => {
        unsafe {
            let a = $a;
            let b = $b;

            if b == 0 {
                ::core::intrinsics::abort()
            } else {
                ::core::intrinsics::unchecked_rem(a, b)
            }
        }
    }
}

macro_rules! srem {
    ($sty:ty, $a:expr, $b:expr) => {
        unsafe {
            let a = $a;
            let b = $b;

            if b == 0 || (b == -1 && a == $sty::min_value()) {
                ::core::intrinsics::abort()
            } else {
                ::core::intrinsics::unchecked_rem(a, b)
            }
        }
    }
}

#[cfg(test)]
#[macro_use]
extern crate quickcheck;

#[cfg(test)]
extern crate core;

#[cfg(test)]
extern crate gcc_s;

#[cfg(test)]
extern crate compiler_rt;

#[cfg(test)]
extern crate rand;

#[cfg(feature = "weak")]
extern crate rlibc;

#[cfg(test)]
#[macro_use]
mod qc;

pub mod int;
pub mod float;

#[cfg(target_arch = "arm")]
pub mod arm;

#[cfg(target_arch = "x86_64")]
pub mod x86_64;
<commit_msg>set crate_name and crate_type in src/lib.rs<commit_after>#![crate_name = "compiler_builtins"]
#![crate_type = "rlib"]
#![feature(asm)]
#![feature(core_intrinsics)]
#![feature(linkage)]
#![feature(naked_functions)]
#![cfg_attr(not(test), no_std)]
#![no_builtins]
// TODO(rust-lang/rust#35021) uncomment when that PR lands
// #![feature(rustc_builtins)]

// We disable #[no_mangle] for tests so that we can verify the test results
// against the native compiler-rt implementations of the builtins.

// NOTE cfg(all(feature = "c", ..)) indicate that compiler-rt provides an arch optimized
// implementation of that intrinsic and we'll prefer to use that

// TODO(rust-lang/rust#37029) use e.g. checked_div(_).unwrap_or_else(|| abort())
macro_rules! udiv {
    ($a:expr, $b:expr) => {
        unsafe {
            let a = $a;
            let b = $b;

            if b == 0 {
                ::core::intrinsics::abort()
            } else {
                ::core::intrinsics::unchecked_div(a, b)
            }
        }
    }
}

macro_rules! sdiv {
    ($sty:ident, $a:expr, $b:expr) => {
        unsafe {
            let a = $a;
            let b = $b;

            if b == 0 || (b == -1 && a == $sty::min_value()) {
                ::core::intrinsics::abort()
            } else {
                ::core::intrinsics::unchecked_div(a, b)
            }
        }
    }
}

macro_rules! urem {
    ($a:expr, $b:expr) => {
        unsafe {
            let a = $a;
            let b = $b;

            if b == 0 {
                ::core::intrinsics::abort()
            } else {
                ::core::intrinsics::unchecked_rem(a, b)
            }
        }
    }
}

macro_rules! srem {
    ($sty:ty, $a:expr, $b:expr) => {
        unsafe {
            let a = $a;
            let b = $b;

            if b == 0 || (b == -1 && a == $sty::min_value()) {
                ::core::intrinsics::abort()
            } else {
                ::core::intrinsics::unchecked_rem(a, b)
            }
        }
    }
}

#[cfg(test)]
#[macro_use]
extern crate quickcheck;

#[cfg(test)]
extern crate core;

#[cfg(test)]
extern crate gcc_s;

#[cfg(test)]
extern crate compiler_rt;

#[cfg(test)]
extern crate rand;

#[cfg(feature = "weak")]
extern crate rlibc;

#[cfg(test)]
#[macro_use]
mod qc;

pub mod int;
pub mod float;

#[cfg(target_arch = "arm")]
pub mod arm;

#[cfg(target_arch = "x86_64")]
pub mod x86_64;
<|endoftext|>


<commit_before>/*!
Miscellaneous utilities grab-bag.

 */

#![crate_name="calx"]

#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate time;
extern crate rand;
extern crate num;
extern crate vec_map;
extern crate nalgebra;
extern crate image;
extern crate cpal;
extern crate bincode;

#[macro_use] extern crate glium;

#[macro_use] extern crate calx_ecs;

use num::{Float};
use std::path::{Path, PathBuf};
use std::ops::{Add, Sub, Mul};

pub use rgb::{Rgba, SRgba, color, scolor, NAMED_COLORS};
pub use fs::{PathExt};
pub use geom::{V2, V3, Rect, RectIter, IterTiles};
pub use img::{color_key, ImageStore};
pub use atlas::{AtlasBuilder, Atlas, AtlasItem};
pub use search::{LatticeNode, Dijkstra, astar_path_with};
pub use hex::{HexGeom, Dir6, HexFov};
pub use index_cache::{IndexCache, CacheKey};
pub use kernel::{Kernel, KernelTerrain};
pub use projection::{Projection};
pub use rng::{EncodeRng, RngExt};

mod atlas;
mod brush;
mod fs;
mod geom;
mod hex;
mod img;
mod index_cache;
mod kernel;
mod projection;
mod rgb;
mod rng;
mod search;

pub mod audio;
pub mod backend;
pub mod debug;
pub mod ease;
pub mod text;
pub mod timing;
pub mod vorud;

#[cfg(test)]
mod test_ecs;

/// Clamp a value to range.
pub fn clamp<C: PartialOrd+Copy>(mn: C, mx: C, x: C) -> C {
    if x < mn { mn }
    else if x > mx { mx }
    else { x }
}

/// Deterministic noise.
pub fn noise(n: i32) -> f32 {
    use std::num::Wrapping;

    let n = Wrapping(n);
    let n = (n << 13) ^ n;
    let m = (n * (n * n * Wrapping(15731) + Wrapping(789221)) + Wrapping(1376312589))
        & Wrapping(0x7fffffff);
    let Wrapping(m) = m;
    1.0 - m as f32 / 1073741824.0
}

/// Convert probability to a log odds deciban value.
///
/// Log odds correspond to the Bayesian probability for a hypothesis that
/// has decibans * 1/10 log_2(10) bits of evidence in favor of it. They're
/// a bit like rolling a d20 but better.
pub fn to_log_odds(p: f32) -> f32 {
    10.0 * (p / (1.0 - p)).log(10.0)
}

/// Convert a log odds deciban value to the corresponding probability.
pub fn from_log_odds(db: f32) -> f32 {
    (1.0 - 1.0 / (1.0 + 10.0.powf(db / 10.0)))
}

/// Interpolate linearly between two values.
pub fn lerp<T, U>(a: U, b: U, t: T) -> U where
        U: Add<U, Output=U> + Sub<U, Output=U> + Mul<T, Output=U> + Copy {
    a + (b - a) * t
}

/// Return the two arguments sorted to order.
fn sorted_pair<T: PartialOrd>(a: T, b: T) -> (T, T) { if a < b { (a, b) } else { (b, a) } }

/// Rectangle anchoring points.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Anchor {
    TopLeft,
    TopRight,
    BottomLeft,
    BottomRight,
    Top,
    Left,
    Right,
    Bottom,
    Center
}

/// Return the application data directory path for the current platform.
pub fn app_data_path(app_name: &str) -> PathBuf {
    use std::env;
    // On Windows, a portable application is just an .exe the user downloads
    // and drops somewhere. The convention here is for a portable application
    // to add its files to wherever its exe file is. An installed application
    // uses an actual installer program and deploys its files to user data
    // directories.
    let is_portable_application = true;

    // TODO: Handle not having the expected env variables.
    if cfg!(windows) {
        if is_portable_application {
            match env::current_exe() {
                Ok(mut p) => { p.pop(); p }
                // If couldn't get self exe path, just use the local relative path and
                // hope for the best.
                _ => Path::new(".").to_path_buf()
            }
        } else {
            Path::new(
                &format!("{}\\{}", env::var("APPDATA").unwrap(), app_name))
            .to_path_buf()
        }
    } else if cfg!(macos) {
        Path::new(
            &format!("{}/Library/Application Support/{}",
                    env::var("HOME").unwrap(), app_name))
        .to_path_buf()
    } else {
        Path::new(
            &format!("{}/.config/{}", env::var("HOME").unwrap(), app_name))
        .to_path_buf()
    }
}

/// Exponential moving average duration.
pub struct AverageDuration {
    weight: f64,
    last_time: f64,
    pub value: f64,
}

impl AverageDuration {
    /// Init is the initial value for the duration, somewhere in the scale you
    /// expect the actual values to be. Weight is between 0 and 1 and
    /// indicates how fast the older values should decay. Weight 1.0 causes
    /// old values to decay immediately.
    pub fn new(init: f64, weight: f64) -> AverageDuration {
        assert!(weight > 0.0 && weight <= 1.0);
        AverageDuration {
            weight: weight,
            last_time: time::precise_time_s(),
            value: init,
        }
    }

    pub fn tick(&mut self) {
        let t = time::precise_time_s();
        self.value = self.weight * (t - self.last_time) + (1.0 - self.weight) * self.value;
        self.last_time = t;
    }
}

#[macro_export]
macro_rules! count_exprs {
    () => { 0 };
    ($e:expr) => { 1 };
    ($e:expr, $($es:expr),+) => { 1 + count_exprs!($($es),*) };
}

#[cfg(test)]
mod test {
    #[test]
    fn test_noise() {
        use super::noise;

        for i in 0i32..100 {
            assert!(noise(i) >= -1.0 && noise(i) <= 1.0);
        }
    }

    #[test]
    fn test_log_odds() {
        use super::{to_log_odds, from_log_odds};
        assert_eq!(from_log_odds(0.0), 0.5);
        assert_eq!(to_log_odds(0.5), 0.0);

        assert_eq!((from_log_odds(-5.0) * 100.0) as i32, 24);
        assert_eq!(to_log_odds(0.909091) as i32, 10);
    }
}
<commit_msg>Remove vorud fixup<commit_after>/*!
Miscellaneous utilities grab-bag.

 */

#![crate_name="calx"]

#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate time;
extern crate rand;
extern crate num;
extern crate vec_map;
extern crate nalgebra;
extern crate image;
extern crate cpal;
extern crate bincode;

#[macro_use] extern crate glium;

#[macro_use] extern crate calx_ecs;

use num::{Float};
use std::path::{Path, PathBuf};
use std::ops::{Add, Sub, Mul};

pub use rgb::{Rgba, SRgba, color, scolor, NAMED_COLORS};
pub use fs::{PathExt};
pub use geom::{V2, V3, Rect, RectIter, IterTiles};
pub use img::{color_key, ImageStore};
pub use atlas::{AtlasBuilder, Atlas, AtlasItem};
pub use search::{LatticeNode, Dijkstra, astar_path_with};
pub use hex::{HexGeom, Dir6, HexFov};
pub use index_cache::{IndexCache, CacheKey};
pub use kernel::{Kernel, KernelTerrain};
pub use projection::{Projection};
pub use rng::{EncodeRng, RngExt};

mod atlas;
mod brush;
mod fs;
mod geom;
mod hex;
mod img;
mod index_cache;
mod kernel;
mod projection;
mod rgb;
mod rng;
mod search;

pub mod audio;
pub mod backend;
pub mod debug;
pub mod ease;
pub mod text;
pub mod timing;

#[cfg(test)]
mod test_ecs;

/// Clamp a value to range.
pub fn clamp<C: PartialOrd+Copy>(mn: C, mx: C, x: C) -> C {
    if x < mn { mn }
    else if x > mx { mx }
    else { x }
}

/// Deterministic noise.
pub fn noise(n: i32) -> f32 {
    use std::num::Wrapping;

    let n = Wrapping(n);
    let n = (n << 13) ^ n;
    let m = (n * (n * n * Wrapping(15731) + Wrapping(789221)) + Wrapping(1376312589))
        & Wrapping(0x7fffffff);
    let Wrapping(m) = m;
    1.0 - m as f32 / 1073741824.0
}

/// Convert probability to a log odds deciban value.
///
/// Log odds correspond to the Bayesian probability for a hypothesis that
/// has decibans * 1/10 log_2(10) bits of evidence in favor of it. They're
/// a bit like rolling a d20 but better.
pub fn to_log_odds(p: f32) -> f32 {
    10.0 * (p / (1.0 - p)).log(10.0)
}

/// Convert a log odds deciban value to the corresponding probability.
pub fn from_log_odds(db: f32) -> f32 {
    (1.0 - 1.0 / (1.0 + 10.0.powf(db / 10.0)))
}

/// Interpolate linearly between two values.
pub fn lerp<T, U>(a: U, b: U, t: T) -> U where
        U: Add<U, Output=U> + Sub<U, Output=U> + Mul<T, Output=U> + Copy {
    a + (b - a) * t
}

/// Return the two arguments sorted to order.
fn sorted_pair<T: PartialOrd>(a: T, b: T) -> (T, T) { if a < b { (a, b) } else { (b, a) } }

/// Rectangle anchoring points.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Anchor {
    TopLeft,
    TopRight,
    BottomLeft,
    BottomRight,
    Top,
    Left,
    Right,
    Bottom,
    Center
}

/// Return the application data directory path for the current platform.
pub fn app_data_path(app_name: &str) -> PathBuf {
    use std::env;
    // On Windows, a portable application is just an .exe the user downloads
    // and drops somewhere. The convention here is for a portable application
    // to add its files to wherever its exe file is. An installed application
    // uses an actual installer program and deploys its files to user data
    // directories.
    let is_portable_application = true;

    // TODO: Handle not having the expected env variables.
    if cfg!(windows) {
        if is_portable_application {
            match env::current_exe() {
                Ok(mut p) => { p.pop(); p }
                // If couldn't get self exe path, just use the local relative path and
                // hope for the best.
                _ => Path::new(".").to_path_buf()
            }
        } else {
            Path::new(
                &format!("{}\\{}", env::var("APPDATA").unwrap(), app_name))
            .to_path_buf()
        }
    } else if cfg!(macos) {
        Path::new(
            &format!("{}/Library/Application Support/{}",
                    env::var("HOME").unwrap(), app_name))
        .to_path_buf()
    } else {
        Path::new(
            &format!("{}/.config/{}", env::var("HOME").unwrap(), app_name))
        .to_path_buf()
    }
}

/// Exponential moving average duration.
pub struct AverageDuration {
    weight: f64,
    last_time: f64,
    pub value: f64,
}

impl AverageDuration {
    /// Init is the initial value for the duration, somewhere in the scale you
    /// expect the actual values to be. Weight is between 0 and 1 and
    /// indicates how fast the older values should decay. Weight 1.0 causes
    /// old values to decay immediately.
    pub fn new(init: f64, weight: f64) -> AverageDuration {
        assert!(weight > 0.0 && weight <= 1.0);
        AverageDuration {
            weight: weight,
            last_time: time::precise_time_s(),
            value: init,
        }
    }

    pub fn tick(&mut self) {
        let t = time::precise_time_s();
        self.value = self.weight * (t - self.last_time) + (1.0 - self.weight) * self.value;
        self.last_time = t;
    }
}

#[macro_export]
macro_rules! count_exprs {
    () => { 0 };
    ($e:expr) => { 1 };
    ($e:expr, $($es:expr),+) => { 1 + count_exprs!($($es),*) };
}

#[cfg(test)]
mod test {
    #[test]
    fn test_noise() {
        use super::noise;

        for i in 0i32..100 {
            assert!(noise(i) >= -1.0 && noise(i) <= 1.0);
        }
    }

    #[test]
    fn test_log_odds() {
        use super::{to_log_odds, from_log_odds};
        assert_eq!(from_log_odds(0.0), 0.5);
        assert_eq!(to_log_odds(0.5), 0.0);

        assert_eq!((from_log_odds(-5.0) * 100.0) as i32, 24);
        assert_eq!(to_log_odds(0.909091) as i32, 10);
    }
}
<|endoftext|>


<commit_before>#![crate_type = "lib"]

#![crate_name = "histogram"]

/*
	This crate is inspired by HdrHistogram.

	WORK IN PROGRESS
*/

#![feature(test)]
extern crate test;

use std::collections::HashMap;

// TODO(brayniac): u64 is likely overkill
// TODO(brayniac): add other counters
pub struct Histogram {
    total: u64,
    precision: u32,
    inner_buckets: u64,
    max_value: u64,
    data: [u64],
}

struct Bucket {
    outer: u64,
    inner: u64,
}

impl Histogram {
    /// return a new Histogram
    pub fn new(precision: u32, max_value: u64) -> Option<Histogram> {
        let inner_buckets = 10_u64.pow(precision)
        let outer_buckets = max_value as f64;
        outerbuckets = outer_buckets.log2().ceil() as u64;

        let total_buckets = inner_buckets * outer_buckets;

        Some(Histogram {
            total: 0,
            data: HashMap::new(),
            precision: precision,
            inner_buckets: 10_u64.pow(precision)
        })
    }

    /// increment counters for value's bucket
    pub fn increment(&mut self, value: u64) {
        self.total += 1;
        match self.get_bucket(value) {
            Ok(bucket) => {
                let count = self.data.entry(bucket.outer).or_insert(HashMap::new()).entry(bucket.inner).or_insert(0);
                *count += 1;
            },
            Err(_) => {

            }
        }
    }

    /// get the count at some value
    pub fn get(&mut self, value: u64) -> u64 {
        match self.get_bucket(value) {
            Ok(bucket) => {
                match self.data.get(&bucket.outer) {
                    Some(outer) => {
                        match outer.get(&bucket.inner) {
                            Some(count) => *count,
                            None => 0,
                        }
                    },
                    None => 0,
                }
            },
            Err(_) => 0
        }
    }

    fn get_bucket_old(&mut self, value: u64) -> Result<Bucket, &'static str> {
        if value == 0 {
            return Err("value too small");
        }

        let v = value as f64;

        // outer bucket is log2(n)
        let outer = v.log2().floor();

         // inner is linearly scaled between 2^(outer) and 2**(outer+1)
        let inner = (v / 2.0_f64.powf(outer) - 1.0_f64) * (self.inner_buckets as f64);
        Ok(Bucket {
            outer: outer.floor() as u64,
            inner: inner.ceil() as u64,
        })

    }

    fn get_bucket(&mut self, value: u64) -> Result<Bucket, &'static str> {

        if value == 0 {
            return Err("value too small");
        }

        let v = value as f64;

        let outer = 63 - value.leading_zeros();

        let remaining = (value - 2_u64.pow(outer));

        // inner is linearly scaled between 2^(outer) and 2**(outer+1)
        let inner = (remaining as f64 / 2_u64.pow(outer) as f64) * (self.inner_buckets as f64);

        Ok(Bucket {
            outer: outer as u64,
            inner: inner.ceil() as u64,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::{Histogram};
    use test::Bencher;

    #[test]
    fn it_works() {
    }

    #[test]
    fn test_new() {
        let histogram = Histogram::new(1).unwrap();

        assert!(histogram.total == 0);
    }

    #[test]
    fn test_increment() {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(0);
        assert!(histogram.total == 1);
        histogram.increment(0);
        assert!(histogram.total == 2);
    }

    #[test]
    fn test_get() {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(1);
        assert!(histogram.get(1) == 1);

        histogram.increment(1);
        assert!(histogram.get(1) == 2);

        histogram.increment(2);
        assert!(histogram.get(2) == 1);

        assert!(histogram.get(3) == 0);
    }

    #[test]
    fn test_get_bucket() {
        let mut histogram = Histogram::new(1).unwrap();

        match histogram.get_bucket(0) {
            Ok(_) => assert!(false, "value 0 shouldn't have a Bucket"),
            Err(e) => assert_eq!(e, "value too small"),
        }

        let bucket = histogram.get_bucket(1).unwrap();
        assert_eq!(bucket.outer, 0);
        assert_eq!(bucket.inner, 0);

        let bucket = histogram.get_bucket(2).unwrap();
        assert_eq!(bucket.outer, 1);
        assert_eq!(bucket.inner, 0);

        let bucket = histogram.get_bucket(3).unwrap();
        assert_eq!(bucket.outer, 1);
        assert_eq!(bucket.inner, 5);

        let bucket = histogram.get_bucket(1023).unwrap();
        assert_eq!(bucket.outer, 9);
        assert_eq!(bucket.inner, 10);

        let bucket = histogram.get_bucket(1024).unwrap();
        assert_eq!(bucket.outer, 10);
        assert_eq!(bucket.inner, 0);

        let bucket = histogram.get_bucket(1025).unwrap();
        assert_eq!(bucket.outer, 10);
        assert_eq!(bucket.inner, 1);
    }

    #[bench]
    fn bench_get_bucket(b: &mut Bencher) {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(1);

        b.iter(|| {
            histogram.get_bucket(1)
        })

    }

    #[bench]
    fn bench_get_bucket_max(b: &mut Bencher) {
        let mut histogram = Histogram::new(10).unwrap();

        histogram.increment(3600000000);

        b.iter(|| {
            histogram.get_bucket(3600000000)
        })

    }

    #[bench]
    fn bench_get_bucket_old(b: &mut Bencher) {
        let mut histogram = Histogram::new(10).unwrap();

        histogram.increment(1);

        b.iter(|| {
            histogram.get_bucket_old(1)
        })

    }

    #[bench]
    fn bench_get_bucket_old_max(b: &mut Bencher) {
        let mut histogram = Histogram::new(10).unwrap();

        histogram.increment(3600000000);

        b.iter(|| {
            histogram.get_bucket_old(3600000000)
        })

    }

    #[bench]
    fn bench_get(b: &mut Bencher) {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(1);

        b.iter(|| {
            histogram.get(1)
        })

    }

    #[bench]
    fn bench_increment(b: &mut Bencher) {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(1);

        b.iter(|| {
            histogram.increment(1)
        })

    }

    #[bench]
    fn bench_incremen_max(b: &mut Bencher) {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(3600000000);

        b.iter(|| {
            histogram.increment(3600000000)
        })

    }
}
<commit_msg>refactor and optimize<commit_after>#![crate_type = "lib"]

#![crate_name = "histogram"]

/*
	This crate is inspired by HdrHistogram.

	WORK IN PROGRESS
*/

#![feature(vec_resize)]
#![feature(test)]

extern crate test;

// TODO(brayniac): u64 is likely overkill
// TODO(brayniac): add other counters

#[allow(dead_code)]
pub struct Histogram {
    total: u64,
    precision: u32,
    inner_buckets: u64,
    max_value: u64,
    data: Vec<u64>,
}

impl Histogram {
    /// return a new Histogram
    pub fn new(precision: u32) -> Option<Histogram> {
        let max_value = 3600000000;
        let inner_buckets = 10_u64.pow(precision);
        let outer_buckets = max_value as f64;
        let outer_buckets = outer_buckets.log2().ceil() as u64;

        let total_buckets = (inner_buckets * outer_buckets) as usize;

        let mut data = Vec::with_capacity(total_buckets);
        data.resize(total_buckets, 0);

        Some(Histogram {
            total: 0,
            data: data,
            precision: precision,
            inner_buckets: inner_buckets,
            max_value: max_value,
        })
    }

    /// increment counters for value's bucket
    pub fn increment(&mut self, value: u64) {
        self.total += 1;
        let index = self.get_index(value);
        self.data[index] += 1;
    }

    /// get the count at some value
    pub fn get(&mut self, value: u64) -> u64 {
        let index = self.get_index(value);
        self.data[index]
    }

    pub fn get_index_a(&mut self, value: u64) -> usize {
        let v = value as f64;
        let outer = v.log2().floor();

        let inner = (v / 2.0_f64.powf(outer) - 1.0_f64) * (self.inner_buckets as f64);

        let outer = outer as u64;
        let inner = inner.ceil() as u64;
        let index = outer * self.inner_buckets + inner;
        index as usize
    }

    pub fn get_index(&mut self, value: u64) -> usize {

        let mut index = 0;

        if value >= 1 {
            let outer = 63 - value.leading_zeros();
            let inner = (value as f64 / 2.0_f64.powi(outer as i32) - 1.0_f64) * (self.inner_buckets as f64);

            let outer = outer as u64;
            let inner = inner.ceil() as u64;
            index = (outer * self.inner_buckets) + inner + 1;
        }

        index as usize
    }
}

#[cfg(test)]
mod tests {
    use super::{Histogram};
    use test::Bencher;

    #[test]
    fn it_works() {
    }

    #[test]
    fn test_new() {
        let histogram = Histogram::new(6).unwrap();

        assert!(histogram.total == 0);
    }

    #[test]
    fn test_increment() {
        let mut histogram = Histogram::new(6).unwrap();

        histogram.increment(0);
        assert!(histogram.total == 1);
        histogram.increment(0);
        assert!(histogram.total == 2);
    }

    #[test]
    fn test_get() {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(1);
        assert!(histogram.get(1) == 1);

        histogram.increment(1);
        assert!(histogram.get(1) == 2);

        histogram.increment(2);
        assert!(histogram.get(2) == 1);

        assert!(histogram.get(3) == 0);
    }

    #[test]
    fn test_get_index() {
        let mut histogram = Histogram::new(3).unwrap();

        // subtract 1,
        // find % 10^precision

        assert_eq!(histogram.get_index(1), 1); // 2^0 = 1

        assert_eq!(histogram.get_index(2), 1001); // 2^1 = 2
        assert_eq!(histogram.get_index(3), 1501); // 2^1 + .5 *2 = 3

        assert_eq!(histogram.get_index(1023), 10000); //=1024 + 0 * 1024
        assert_eq!(histogram.get_index(1024), 10001); //= " "
        assert_eq!(histogram.get_index(1025), 10002); //= 1024 + .1 * 1024 = 11

    }

    #[bench]
    fn bench_get_small_value(b: &mut Bencher) {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(1);

        b.iter(|| {
            histogram.get(1)
        })

    }

    #[bench]
    fn bench_get_large_value(b: &mut Bencher) {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(3600000000);

        b.iter(|| {
            histogram.get(3600000000)
        })

    }

    #[bench]
    fn bench_increment_small_value(b: &mut Bencher) {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(1);

        b.iter(|| {
            histogram.increment(1)
        })

    }

    #[bench]
    fn bench_increment_large_value(b: &mut Bencher) {
        let mut histogram = Histogram::new(1).unwrap();

        histogram.increment(3600000000);

        b.iter(|| {
            histogram.increment(3600000000)
        })

    }

    #[bench]
    fn bench_get_index_small_value(b: &mut Bencher) {
        let mut histogram = Histogram::new(6).unwrap();

        b.iter(|| {
            histogram.get_index(1);
        })
    }

    #[bench]
    fn bench_get_index_large_value(b: &mut Bencher) {
        let mut histogram = Histogram::new(6).unwrap();

        b.iter(|| {
            histogram.get_index(3600000000);
        })
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The android_logger Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

//! A logger which writes to android output.
//!
//! ## Example
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::Level;
//! use android_logger::Config;
//!
//! /// Android code may not have obvious "main", this is just an example.
//! fn main() {
//!     android_logger::init_once(
//!         Config::default().with_min_level(Level::Trace),
//!     );
//!
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//! }
//! ```
//!
//! ## Example with module path filter
//!
//! It is possible to limit log messages to output from a specific crate,
//! and override the logcat tag name (by default, the crate name is used):
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::Level;
//! use android_logger::{Config,FilterBuilder};
//!
//! fn main() {
//!     android_logger::init_once(
//!         Config::default()
//!             .with_min_level(Level::Trace)
//!             .with_tag("mytag")
//!             .with_filter(FilterBuilder::new().parse("debug,hello::crate=trace").build()),
//!     );
//!
//!     // ..
//! }
//! ```

#[cfg(target_os = "android")]
extern crate android_log_sys as log_ffi;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

extern crate env_logger;

use std::sync::RwLock;

#[cfg(target_os = "android")]
use log_ffi::LogPriority;
use log::{Level, Log, Metadata, Record};
use std::ffi::{CStr, CString};
use std::mem;
use std::fmt;
use std::ptr;

pub use env_logger::filter::{Filter, Builder as FilterBuilder};

/// Output log to android system.
#[cfg(target_os = "android")]
fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
    unsafe {
        log_ffi::__android_log_write(
            prio as log_ffi::c_int,
            tag.as_ptr() as *const log_ffi::c_char,
            msg.as_ptr() as *const log_ffi::c_char,
        )
    };
}

/// Dummy output placeholder for tests.
#[cfg(not(target_os = "android"))]
fn android_log(_priority: Level, _tag: &CStr, _msg: &CStr) {}

/// Underlying android logger backend
pub struct AndroidLogger {
    config: RwLock<Config>,
}

lazy_static! {
   static ref ANDROID_LOGGER: AndroidLogger = AndroidLogger::default();
}

const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;

impl Default for AndroidLogger {
    /// Create a new logger with default config
    fn default() -> AndroidLogger {
        AndroidLogger {
            config: RwLock::new(Config::default()),
        }
    }
}

impl Log for AndroidLogger {
    fn enabled(&self, _: &Metadata) -> bool {
        true
    }

    fn log(&self, record: &Record) {
        let config = self.config
            .read()
            .expect("failed to acquire android_log filter lock for read");

        if !config.filter_matches(record) {
            return;
        }

        // tag must not exceed LOGGING_TAG_MAX_LEN
        let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };

        let module_path = record.module_path().unwrap_or_default().to_owned();

        // If no tag was specified, use module name
        let custom_tag = &config.tag;
        let tag = custom_tag.as_ref().map(|s| s.as_bytes()).unwrap_or(module_path.as_bytes());

        // truncate the tag here to fit into LOGGING_TAG_MAX_LEN
        self.fill_tag_bytes(&mut tag_bytes, tag);
        // use stack array as C string
        let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };

        // message must not exceed LOGGING_MSG_MAX_LEN
        // therefore split log message into multiple log calls
        let mut writer = PlatformLogWriter::new(record.level(), tag);

        // If a custom tag is used, add the module path to the message.
        // Use PlatformLogWriter to output chunks if they exceed max size.
        let _ = if custom_tag.is_some() {
            fmt::write(&mut writer, format_args!("{}: {}", module_path, *record.args()))
        } else {
            fmt::write(&mut writer, *record.args())
        };

        // output the remaining message (this would usually be the most common case)
        writer.flush();
    }

    fn flush(&self) {}
}

impl AndroidLogger {
    fn fill_tag_bytes(&self, array: &mut [u8], tag: &[u8]) {
        if tag.len() > LOGGING_TAG_MAX_LEN {
            for (input, output) in tag.iter()
                .take(LOGGING_TAG_MAX_LEN - 2)
                .chain(b"..\0".iter())
                .zip(array.iter_mut())
            {
                *output = *input;
            }
        } else {
            for (input, output) in tag.iter()
                .chain(b"\0".iter())
                .zip(array.iter_mut())
            {
                *output = *input;
            }
        }
    }
}

/// Filter for android logger.
pub struct Config {
    log_level: Option<Level>,
    filter: Option<env_logger::filter::Filter>,
    tag: Option<CString>,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            log_level: None,
            filter: None,
            tag: None,
        }
    }
}

impl Config {
    /// Change the minimum log level.
    ///
    /// All values above the set level are logged. For example, if
    /// `Warn` is set, the `Error` is logged too, but `Info` isn't.
    pub fn with_min_level(mut self, level: Level) -> Self {
        self.log_level = Some(level);
        self
    }

    fn filter_matches(&self, record: &Record) -> bool {
        if let Some(ref filter) = self.filter {
            filter.matches(&record)
        } else {
            true
        }
    }

    pub fn with_filter(mut self, filter: env_logger::filter::Filter) -> Self {
        self.filter = Some(filter);
        self
    }

    pub fn with_tag<S: Into<Vec<u8>>>(mut self, tag: S) -> Self {
        self.tag = Some(CString::new(tag).expect("Can't convert tag to CString"));
        self
    }
}

struct PlatformLogWriter<'a> {
    #[cfg(target_os = "android")] priority: LogPriority,
    #[cfg(not(target_os = "android"))] priority: Level,
    len: usize,
    last_newline_index: usize,
    tag: &'a CStr,
    buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
}

impl<'a> PlatformLogWriter<'a> {
    #[cfg(target_os = "android")]
    pub fn new(level: Level, tag: &CStr) -> PlatformLogWriter {
        PlatformLogWriter {
            priority: match level {
                Level::Warn => LogPriority::WARN,
                Level::Info => LogPriority::INFO,
                Level::Debug => LogPriority::DEBUG,
                Level::Error => LogPriority::ERROR,
                Level::Trace => LogPriority::VERBOSE,
            },
            len: 0,
            last_newline_index: 0,
            tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    #[cfg(not(target_os = "android"))]
    pub fn new(level: Level, tag: &CStr) -> PlatformLogWriter {
        PlatformLogWriter {
            priority: level,
            len: 0,
            last_newline_index: 0,
            tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    /// Flush some bytes to android logger.
    ///
    /// If there is a newline, flush up to it.
    /// If ther was no newline, flush all.
    ///
    /// Not guaranteed to flush everything.
    fn temporal_flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        if self.last_newline_index > 0 {
            let copy_from_index = self.last_newline_index;
            let remaining_chunk_len = total_len - copy_from_index;

            self.output_specified_len(copy_from_index);
            self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
            self.len = remaining_chunk_len;
        } else {
            self.output_specified_len(total_len);
            self.len = 0;
        }
        self.last_newline_index = 0;
    }

    /// Flush everything remaining to android logger.
    fn flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        self.output_specified_len(total_len);
        self.len = 0;
        self.last_newline_index = 0;
    }

    /// Output buffer up until the \0 which will be placed at `len` position.
    fn output_specified_len(&mut self, len: usize) {
        let mut last_byte: u8 = b'\0';
        mem::swap(&mut last_byte, unsafe {
            self.buffer.get_unchecked_mut(len)
        });

        let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
        android_log(self.priority, self.tag, msg);

        *unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
    }

    /// Copy `len` bytes from `index` position to starting position.
    fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
        let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
        let dst = self.buffer.as_mut_ptr();
        unsafe { ptr::copy(src, dst, len) };
    }
}

impl<'a> fmt::Write for PlatformLogWriter<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let mut incomming_bytes = s.as_bytes();

        while !incomming_bytes.is_empty() {
            let len = self.len;

            // write everything possible to buffer and mark last \n
            let new_len = len + incomming_bytes.len();
            let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN]
                .iter_mut()
                .zip(incomming_bytes)
                .enumerate()
                .fold(None, |acc, (i, (output, input))| {
                    *output = *input;
                    if *input == b'\n' {
                        Some(i)
                    } else {
                        acc
                    }
                });

            // update last \n index
            if let Some(newline) = last_newline {
                self.last_newline_index = len + newline;
            }

            // calculate how many bytes were written
            let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
                // if the len was not exceeded
                self.len = new_len;
                new_len - len // written len
            } else {
                // if new length was exceeded
                self.len = LOGGING_MSG_MAX_LEN;
                self.temporal_flush();

                LOGGING_MSG_MAX_LEN - len // written len
            };

            incomming_bytes = &incomming_bytes[written_len..];
        }

        Ok(())
    }
}

/// Send a log record to Android logging backend.
///
/// This action does not require initialization. However, without initialization it
/// will use the default filter, which allows all logs.
pub fn log(record: &Record) {
    ANDROID_LOGGER.log(record)
}

/// Initializes the global logger with an android logger.
///
/// This can be called many times, but will only initialize logging once,
/// and will not replace any other previously initialized logger.
///
/// It is ok to call this at the activity creation, and it will be
/// repeatedly called on every lifecycle restart (i.e. screen rotation).
pub fn init_once(config: Config) {
    if let Err(err) = log::set_logger(&*ANDROID_LOGGER) {
        debug!("android_logger: log::set_logger failed: {}", err);
    } else {
        if let Some(level) = config.log_level {
            log::set_max_level(level.to_level_filter());
        }
        *ANDROID_LOGGER
            .config
            .write()
            .expect("failed to acquire android_log filter lock for write") = config;
    }
}
<commit_msg>Allow constructing new instances of a logger<commit_after>// Copyright 2016 The android_logger Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

//! A logger which writes to android output.
//!
//! ## Example
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::Level;
//! use android_logger::Config;
//!
//! /// Android code may not have obvious "main", this is just an example.
//! fn main() {
//!     android_logger::init_once(
//!         Config::default().with_min_level(Level::Trace),
//!     );
//!
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//! }
//! ```
//!
//! ## Example with module path filter
//!
//! It is possible to limit log messages to output from a specific crate,
//! and override the logcat tag name (by default, the crate name is used):
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::Level;
//! use android_logger::{Config,FilterBuilder};
//!
//! fn main() {
//!     android_logger::init_once(
//!         Config::default()
//!             .with_min_level(Level::Trace)
//!             .with_tag("mytag")
//!             .with_filter(FilterBuilder::new().parse("debug,hello::crate=trace").build()),
//!     );
//!
//!     // ..
//! }
//! ```

#[cfg(target_os = "android")]
extern crate android_log_sys as log_ffi;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

extern crate env_logger;

use std::sync::RwLock;

#[cfg(target_os = "android")]
use log_ffi::LogPriority;
use log::{Level, Log, Metadata, Record};
use std::ffi::{CStr, CString};
use std::mem;
use std::fmt;
use std::ptr;

pub use env_logger::filter::{Filter, Builder as FilterBuilder};

/// Output log to android system.
#[cfg(target_os = "android")]
fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
    unsafe {
        log_ffi::__android_log_write(
            prio as log_ffi::c_int,
            tag.as_ptr() as *const log_ffi::c_char,
            msg.as_ptr() as *const log_ffi::c_char,
        )
    };
}

/// Dummy output placeholder for tests.
#[cfg(not(target_os = "android"))]
fn android_log(_priority: Level, _tag: &CStr, _msg: &CStr) {}

/// Underlying android logger backend
pub struct AndroidLogger {
    config: RwLock<Config>,
}

impl AndroidLogger {
    /// Create new logger instance from config
    pub fn new(config: Config) -> AndroidLogger {
        AndroidLogger {
            config: RwLock::new(config),
        }
    }
}

lazy_static! {
   static ref ANDROID_LOGGER: AndroidLogger = AndroidLogger::default();
}

const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;

impl Default for AndroidLogger {
    /// Create a new logger with default config
    fn default() -> AndroidLogger {
        AndroidLogger {
            config: RwLock::new(Config::default()),
        }
    }
}

impl Log for AndroidLogger {
    fn enabled(&self, _: &Metadata) -> bool {
        true
    }

    fn log(&self, record: &Record) {
        let config = self.config
            .read()
            .expect("failed to acquire android_log filter lock for read");

        if !config.filter_matches(record) {
            return;
        }

        // tag must not exceed LOGGING_TAG_MAX_LEN
        let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };

        let module_path = record.module_path().unwrap_or_default().to_owned();

        // If no tag was specified, use module name
        let custom_tag = &config.tag;
        let tag = custom_tag.as_ref().map(|s| s.as_bytes()).unwrap_or(module_path.as_bytes());

        // truncate the tag here to fit into LOGGING_TAG_MAX_LEN
        self.fill_tag_bytes(&mut tag_bytes, tag);
        // use stack array as C string
        let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };

        // message must not exceed LOGGING_MSG_MAX_LEN
        // therefore split log message into multiple log calls
        let mut writer = PlatformLogWriter::new(record.level(), tag);

        // If a custom tag is used, add the module path to the message.
        // Use PlatformLogWriter to output chunks if they exceed max size.
        let _ = if custom_tag.is_some() {
            fmt::write(&mut writer, format_args!("{}: {}", module_path, *record.args()))
        } else {
            fmt::write(&mut writer, *record.args())
        };

        // output the remaining message (this would usually be the most common case)
        writer.flush();
    }

    fn flush(&self) {}
}

impl AndroidLogger {
    fn fill_tag_bytes(&self, array: &mut [u8], tag: &[u8]) {
        if tag.len() > LOGGING_TAG_MAX_LEN {
            for (input, output) in tag.iter()
                .take(LOGGING_TAG_MAX_LEN - 2)
                .chain(b"..\0".iter())
                .zip(array.iter_mut())
            {
                *output = *input;
            }
        } else {
            for (input, output) in tag.iter()
                .chain(b"\0".iter())
                .zip(array.iter_mut())
            {
                *output = *input;
            }
        }
    }
}

/// Filter for android logger.
pub struct Config {
    log_level: Option<Level>,
    filter: Option<env_logger::filter::Filter>,
    tag: Option<CString>,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            log_level: None,
            filter: None,
            tag: None,
        }
    }
}

impl Config {
    /// Change the minimum log level.
    ///
    /// All values above the set level are logged. For example, if
    /// `Warn` is set, the `Error` is logged too, but `Info` isn't.
    pub fn with_min_level(mut self, level: Level) -> Self {
        self.log_level = Some(level);
        self
    }

    fn filter_matches(&self, record: &Record) -> bool {
        if let Some(ref filter) = self.filter {
            filter.matches(&record)
        } else {
            true
        }
    }

    pub fn with_filter(mut self, filter: env_logger::filter::Filter) -> Self {
        self.filter = Some(filter);
        self
    }

    pub fn with_tag<S: Into<Vec<u8>>>(mut self, tag: S) -> Self {
        self.tag = Some(CString::new(tag).expect("Can't convert tag to CString"));
        self
    }
}

struct PlatformLogWriter<'a> {
    #[cfg(target_os = "android")] priority: LogPriority,
    #[cfg(not(target_os = "android"))] priority: Level,
    len: usize,
    last_newline_index: usize,
    tag: &'a CStr,
    buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
}

impl<'a> PlatformLogWriter<'a> {
    #[cfg(target_os = "android")]
    pub fn new(level: Level, tag: &CStr) -> PlatformLogWriter {
        PlatformLogWriter {
            priority: match level {
                Level::Warn => LogPriority::WARN,
                Level::Info => LogPriority::INFO,
                Level::Debug => LogPriority::DEBUG,
                Level::Error => LogPriority::ERROR,
                Level::Trace => LogPriority::VERBOSE,
            },
            len: 0,
            last_newline_index: 0,
            tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    #[cfg(not(target_os = "android"))]
    pub fn new(level: Level, tag: &CStr) -> PlatformLogWriter {
        PlatformLogWriter {
            priority: level,
            len: 0,
            last_newline_index: 0,
            tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    /// Flush some bytes to android logger.
    ///
    /// If there is a newline, flush up to it.
    /// If ther was no newline, flush all.
    ///
    /// Not guaranteed to flush everything.
    fn temporal_flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        if self.last_newline_index > 0 {
            let copy_from_index = self.last_newline_index;
            let remaining_chunk_len = total_len - copy_from_index;

            self.output_specified_len(copy_from_index);
            self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
            self.len = remaining_chunk_len;
        } else {
            self.output_specified_len(total_len);
            self.len = 0;
        }
        self.last_newline_index = 0;
    }

    /// Flush everything remaining to android logger.
    fn flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        self.output_specified_len(total_len);
        self.len = 0;
        self.last_newline_index = 0;
    }

    /// Output buffer up until the \0 which will be placed at `len` position.
    fn output_specified_len(&mut self, len: usize) {
        let mut last_byte: u8 = b'\0';
        mem::swap(&mut last_byte, unsafe {
            self.buffer.get_unchecked_mut(len)
        });

        let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
        android_log(self.priority, self.tag, msg);

        *unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
    }

    /// Copy `len` bytes from `index` position to starting position.
    fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
        let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
        let dst = self.buffer.as_mut_ptr();
        unsafe { ptr::copy(src, dst, len) };
    }
}

impl<'a> fmt::Write for PlatformLogWriter<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let mut incomming_bytes = s.as_bytes();

        while !incomming_bytes.is_empty() {
            let len = self.len;

            // write everything possible to buffer and mark last \n
            let new_len = len + incomming_bytes.len();
            let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN]
                .iter_mut()
                .zip(incomming_bytes)
                .enumerate()
                .fold(None, |acc, (i, (output, input))| {
                    *output = *input;
                    if *input == b'\n' {
                        Some(i)
                    } else {
                        acc
                    }
                });

            // update last \n index
            if let Some(newline) = last_newline {
                self.last_newline_index = len + newline;
            }

            // calculate how many bytes were written
            let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
                // if the len was not exceeded
                self.len = new_len;
                new_len - len // written len
            } else {
                // if new length was exceeded
                self.len = LOGGING_MSG_MAX_LEN;
                self.temporal_flush();

                LOGGING_MSG_MAX_LEN - len // written len
            };

            incomming_bytes = &incomming_bytes[written_len..];
        }

        Ok(())
    }
}

/// Send a log record to Android logging backend.
///
/// This action does not require initialization. However, without initialization it
/// will use the default filter, which allows all logs.
pub fn log(record: &Record) {
    ANDROID_LOGGER.log(record)
}

/// Initializes the global logger with an android logger.
///
/// This can be called many times, but will only initialize logging once,
/// and will not replace any other previously initialized logger.
///
/// It is ok to call this at the activity creation, and it will be
/// repeatedly called on every lifecycle restart (i.e. screen rotation).
pub fn init_once(config: Config) {
    if let Err(err) = log::set_logger(&*ANDROID_LOGGER) {
        debug!("android_logger: log::set_logger failed: {}", err);
    } else {
        if let Some(level) = config.log_level {
            log::set_max_level(level.to_level_filter());
        }
        *ANDROID_LOGGER
            .config
            .write()
            .expect("failed to acquire android_log filter lock for write") = config;
    }
}
<|endoftext|>