<commit_before>//! Module for built-in filter functions
//!
//! Contains all the built-in filter functions for use in templates.
//! You can define your own filters; for more information,
//! see the top-level crate documentation.

#[cfg(feature = "serde-json")]
mod json;

#[cfg(feature = "serde-json")]
pub use self::json::json;

use askama_escape::MarkupDisplay;
use error::Error::Fmt;
use humansize::{file_size_opts, FileSize};
use num_traits::cast::NumCast;
use num_traits::Signed;
use std::fmt;

use super::Result;

// This is used by the code generator to decide whether a named filter is part of
// Askama or should refer to a local `filters` module. It should contain all the
// filters shipped with Askama, even the optional ones (since optional inclusion
// in the const vector based on features seems impossible right now).
pub const BUILT_IN_FILTERS: [&str; 22] = [
    "abs",
    "capitalize",
    "center",
    "e",
    "escape",
    "filesizeformat",
    "format",
    "indent",
    "into_f64",
    "into_isize",
    "join",
    "linebreaks",
    "linebreaksbr",
    "lower",
    "lowercase",
    "safe",
    "trim",
    "truncate",
    "upper",
    "uppercase",
    "wordcount",
    "json", // Optional feature; reserve the name anyway
];

/// Marks a string (or other `Display` type) as safe
///
/// Use this is you want to allow markup in an expression, or if you know
/// that the expression's contents don't need to be escaped.
pub fn safe<D, I>(v: I) -> Result<MarkupDisplay<D>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    let res: MarkupDisplay<D> = v.into();
    Ok(res.mark_safe())
}

/// Escapes `&`, `<` and `>` in strings
pub fn escape<D, I>(i: I) -> Result<MarkupDisplay<D>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    Ok(i.into())
}

/// Alias for the `escape()` filter
pub fn e<D, I>(i: I) -> Result<MarkupDisplay<D>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    escape(i)
}

/// Returns adequate string representation (in KB, ..) of number of bytes
pub fn filesizeformat(b: usize) -> Result<String> {
    b.file_size(file_size_opts::DECIMAL)
        .map_err(|_| Fmt(fmt::Error))
}

/// Formats arguments according to the specified format
///
/// The first argument to this filter must be a string literal (as in normal
/// Rust). All arguments are passed through to the `format!()`
/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
/// the Askama code generator.
pub fn format() {}

/// Replaces line breaks in plain text with appropriate HTML
///
/// A single newline becomes an HTML line break `<br>` and a new line
/// followed by a blank line becomes a paragraph break `<p>`.
pub fn linebreaks(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    let linebroken = s.replace("\n\n", "

").replace("\n", "
");

    Ok(format!("
{}
", linebroken))
}

/// Converts all newlines in a piece of plain text to HTML line breaks
pub fn linebreaksbr(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.replace("\n", "
"))
}

/// Converts to lowercase
pub fn lower(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.to_lowercase())
}

/// Alias for the `lower()` filter
pub fn lowercase(s: &fmt::Display) -> Result<String> {
    lower(s)
}

/// Converts to uppercase
pub fn upper(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.to_uppercase())
}

/// Alias for the `upper()` filter
pub fn uppercase(s: &fmt::Display) -> Result<String> {
    upper(s)
}

/// Strip leading and trailing whitespace
pub fn trim(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.trim().to_owned())
}

/// Limit string length, appends '...' if truncated
pub fn truncate(s: &fmt::Display, len: &usize) -> Result<String> {
    let mut s = s.to_string();
    if s.len() < *len {
        Ok(s)
    } else {
        s.truncate(*len);
        s.push_str("...");
        Ok(s)
    }
}

/// Indent lines with `width` spaces
pub fn indent(s: &fmt::Display, width: &usize) -> Result<String> {
    let s = s.to_string();

    let mut indented = String::new();

    for (i, c) in s.char_indices() {
        indented.push(c);

        if c == '\n' && i < s.len() - 1 {
            for _ in 0..*width {
                indented.push(' ');
            }
        }
    }

    Ok(indented)
}

/// Casts number to f64
pub fn into_f64<T>(number: T) -> Result<f64>
where
    T: NumCast,
{
    number.to_f64().ok_or(Fmt(fmt::Error))
}

/// Casts number to isize
pub fn into_isize<T>(number: T) -> Result<isize>
where
    T: NumCast,
{
    number.to_isize().ok_or(Fmt(fmt::Error))
}

/// Joins iterable into a string separated by provided argument
pub fn join<T, I, S>(input: I, separator: S) -> Result<String>
where
    T: fmt::Display,
    I: Iterator<Item = T>,
    S: AsRef<str>,
{
    let separator: &str = separator.as_ref();

    let mut rv = String::new();

    for (num, item) in input.enumerate() {
        if num > 0 {
            rv.push_str(separator);
        }

        rv.push_str(&format!("{}", item));
    }

    Ok(rv)
}

/// Absolute value
pub fn abs<T>(number: T) -> Result<T>
where
    T: Signed,
{
    Ok(number.abs())
}

/// Capitalize a value. The first character will be uppercase, all others lowercase.
pub fn capitalize(s: &fmt::Display) -> Result<String> {
    let mut s = s.to_string();

    match s.get_mut(0..1).map(|s| {
        s.make_ascii_uppercase();
        &*s
    }) {
        None => Ok(s),
        _ => {
            let l = s.len();
            match s.get_mut(1..l).map(|s| {
                s.make_ascii_lowercase();
                &*s
            }) {
                _ => Ok(s),
            }
        }
    }
}

/// Centers the value in a field of a given width
pub fn center(s: &fmt::Display, l: usize) -> Result<String> {
    let s = s.to_string();
    let len = s.len();

    if l <= len {
        Ok(s)
    } else {
        let p = l - len;
        let q = p / 2;
        let r = p % 2;
        let mut buf = String::with_capacity(l);

        for _ in 0..q {
            buf.push(' ');
        }

        buf.push_str(&s);

        for _ in 0..q + r {
            buf.push(' ');
        }

        Ok(buf)
    }
}

/// Count the words in that string
pub fn wordcount(s: &fmt::Display) -> Result<usize> {
    let s = s.to_string();

    Ok(s.split_whitespace().count())
}

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

    #[test]
    fn test_filesizeformat() {
        assert_eq!(filesizeformat(0).unwrap(), "0 B");
        assert_eq!(filesizeformat(999).unwrap(), "999 B");
        assert_eq!(filesizeformat(1000).unwrap(), "1 KB");
        assert_eq!(filesizeformat(1023).unwrap(), "1.02 KB");
        assert_eq!(filesizeformat(1024).unwrap(), "1.02 KB");
    }

    #[test]
    fn test_linebreaks() {
        assert_eq!(
            linebreaks(&"Foo\nBar Baz").unwrap(),
            "
Foo
Bar Baz
"
        );
        assert_eq!(
            linebreaks(&"Foo\nBar\n\nBaz").unwrap(),
            "
Foo
Bar

Baz
"
        );
    }

    #[test]
    fn test_linebreaksbr() {
        assert_eq!(linebreaksbr(&"Foo\nBar").unwrap(), "Foo
Bar");
        assert_eq!(
            linebreaksbr(&"Foo\nBar\n\nBaz").unwrap(),
            "Foo
Bar

Baz"
        );
    }

    #[test]
    fn test_lower() {
        assert_eq!(lower(&"Foo").unwrap(), "foo");
        assert_eq!(lower(&"FOO").unwrap(), "foo");
        assert_eq!(lower(&"FooBar").unwrap(), "foobar");
        assert_eq!(lower(&"foo").unwrap(), "foo");
    }

    #[test]
    fn test_upper() {
        assert_eq!(upper(&"Foo").unwrap(), "FOO");
        assert_eq!(upper(&"FOO").unwrap(), "FOO");
        assert_eq!(upper(&"FooBar").unwrap(), "FOOBAR");
        assert_eq!(upper(&"foo").unwrap(), "FOO");
    }

    #[test]
    fn test_trim() {
        assert_eq!(trim(&" Hello\tworld\t").unwrap(), "Hello\tworld");
    }

    #[test]
    fn test_indent() {
        assert_eq!(indent(&"hello", &2).unwrap(), "hello");
        assert_eq!(indent(&"hello\n", &2).unwrap(), "hello\n");
        assert_eq!(indent(&"hello\nfoo", &2).unwrap(), "hello\n  foo");
        assert_eq!(
            indent(&"hello\nfoo\n bar", &4).unwrap(),
            "hello\n    foo\n     bar"
        );
    }

    #[test]
    fn test_into_f64() {
        assert_eq!(into_f64(1).unwrap(), 1.0 as f64);
        assert_eq!(into_f64(1.9).unwrap(), 1.9 as f64);
        assert_eq!(into_f64(-1.9).unwrap(), -1.9 as f64);
        assert_eq!(into_f64(INFINITY as f32).unwrap(), INFINITY);
        assert_eq!(into_f64(-INFINITY as f32).unwrap(), -INFINITY);
    }

    #[test]
    fn test_into_isize() {
        assert_eq!(into_isize(1).unwrap(), 1 as isize);
        assert_eq!(into_isize(1.9).unwrap(), 1 as isize);
        assert_eq!(into_isize(-1.9).unwrap(), -1 as isize);
        assert_eq!(into_isize(1.5 as f64).unwrap(), 1 as isize);
        assert_eq!(into_isize(-1.5 as f64).unwrap(), -1 as isize);
        match into_isize(INFINITY) {
            Err(Fmt(fmt::Error)) => assert!(true),
            _ => assert!(false, "Should return error of type Err(Fmt(fmt::Error))"),
        };
    }

    #[test]
    fn test_join() {
        assert_eq!(
            join((&["hello", "world"]).into_iter(), ", ").unwrap(),
            "hello, world"
        );
        assert_eq!(join((&["hello"]).into_iter(), ", ").unwrap(), "hello");

        let empty: &[&str] = &[];
        assert_eq!(join(empty.into_iter(), ", ").unwrap(), "");

        let input: Vec<String> = vec!["foo".into(), "bar".into(), "bazz".into()];
        assert_eq!(
            join((&input).into_iter(), ":".to_string()).unwrap(),
            "foo:bar:bazz"
        );
        assert_eq!(
            join(input.clone().into_iter(), ":").unwrap(),
            "foo:bar:bazz"
        );
        assert_eq!(
            join(input.clone().into_iter(), ":".to_string()).unwrap(),
            "foo:bar:bazz"
        );

        let input: &[String] = &["foo".into(), "bar".into()];
        assert_eq!(join(input.into_iter(), ":").unwrap(), "foo:bar");
        assert_eq!(join(input.into_iter(), ":".to_string()).unwrap(), "foo:bar");

        let real: String = "blah".into();
        let input: Vec<&str> = vec![&real];
        assert_eq!(join(input.into_iter(), ";").unwrap(), "blah");

        assert_eq!(
            join((&&&&&["foo", "bar"]).into_iter(), ", ").unwrap(),
            "foo, bar"
        );
    }

    #[test]
    fn test_abs() {
        assert_eq!(abs(1).unwrap(), 1);
        assert_eq!(abs(-1).unwrap(), 1);
        assert_eq!(abs(1.0).unwrap(), 1.0);
        assert_eq!(abs(-1.0).unwrap(), 1.0);
        assert_eq!(abs(1.0 as f64).unwrap(), 1.0 as f64);
        assert_eq!(abs(-1.0 as f64).unwrap(), 1.0 as f64);
    }

    #[test]
    fn test_capitalize() {
        assert_eq!(capitalize(&"foo").unwrap(), "Foo".to_string());
        assert_eq!(capitalize(&"f").unwrap(), "F".to_string());
        assert_eq!(capitalize(&"fO").unwrap(), "Fo".to_string());
        assert_eq!(capitalize(&"").unwrap(), "".to_string());
        assert_eq!(capitalize(&"FoO").unwrap(), "Foo".to_string());
        assert_eq!(capitalize(&"foO BAR").unwrap(), "Foo bar".to_string());
    }

    #[test]
    fn test_center() {
        assert_eq!(center(&"f", 3).unwrap(), " f ".to_string());
        assert_eq!(center(&"f", 4).unwrap(), " f  ".to_string());
        assert_eq!(center(&"foo", 1).unwrap(), "foo".to_string());
        assert_eq!(center(&"foo bar", 8).unwrap(), "foo bar ".to_string());
    }

    #[test]
    fn test_wordcount() {
        assert_eq!(wordcount(&"").unwrap(), 0);
        assert_eq!(wordcount(&" \n\t").unwrap(), 0);
        assert_eq!(wordcount(&"foo").unwrap(), 1);
        assert_eq!(wordcount(&"foo bar").unwrap(), 2);
    }
}
<commit_msg>Fix unnecessary len at capitalize filter<commit_after>//! Module for built-in filter functions
//!
//! Contains all the built-in filter functions for use in templates.
//! You can define your own filters; for more information,
//! see the top-level crate documentation.

#[cfg(feature = "serde-json")]
mod json;

#[cfg(feature = "serde-json")]
pub use self::json::json;

use askama_escape::MarkupDisplay;
use error::Error::Fmt;
use humansize::{file_size_opts, FileSize};
use num_traits::cast::NumCast;
use num_traits::Signed;
use std::fmt;

use super::Result;

// This is used by the code generator to decide whether a named filter is part of
// Askama or should refer to a local `filters` module. It should contain all the
// filters shipped with Askama, even the optional ones (since optional inclusion
// in the const vector based on features seems impossible right now).
pub const BUILT_IN_FILTERS: [&str; 22] = [
    "abs",
    "capitalize",
    "center",
    "e",
    "escape",
    "filesizeformat",
    "format",
    "indent",
    "into_f64",
    "into_isize",
    "join",
    "linebreaks",
    "linebreaksbr",
    "lower",
    "lowercase",
    "safe",
    "trim",
    "truncate",
    "upper",
    "uppercase",
    "wordcount",
    "json", // Optional feature; reserve the name anyway
];

/// Marks a string (or other `Display` type) as safe
///
/// Use this is you want to allow markup in an expression, or if you know
/// that the expression's contents don't need to be escaped.
pub fn safe<D, I>(v: I) -> Result<MarkupDisplay<D>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    let res: MarkupDisplay<D> = v.into();
    Ok(res.mark_safe())
}

/// Escapes `&`, `<` and `>` in strings
pub fn escape<D, I>(i: I) -> Result<MarkupDisplay<D>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    Ok(i.into())
}

/// Alias for the `escape()` filter
pub fn e<D, I>(i: I) -> Result<MarkupDisplay<D>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    escape(i)
}

/// Returns adequate string representation (in KB, ..) of number of bytes
pub fn filesizeformat(b: usize) -> Result<String> {
    b.file_size(file_size_opts::DECIMAL)
        .map_err(|_| Fmt(fmt::Error))
}

/// Formats arguments according to the specified format
///
/// The first argument to this filter must be a string literal (as in normal
/// Rust). All arguments are passed through to the `format!()`
/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
/// the Askama code generator.
pub fn format() {}

/// Replaces line breaks in plain text with appropriate HTML
///
/// A single newline becomes an HTML line break `<br>` and a new line
/// followed by a blank line becomes a paragraph break `<p>`.
pub fn linebreaks(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    let linebroken = s.replace("\n\n", "

").replace("\n", "
");

    Ok(format!("
{}
", linebroken))
}

/// Converts all newlines in a piece of plain text to HTML line breaks
pub fn linebreaksbr(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.replace("\n", "
"))
}

/// Converts to lowercase
pub fn lower(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.to_lowercase())
}

/// Alias for the `lower()` filter
pub fn lowercase(s: &fmt::Display) -> Result<String> {
    lower(s)
}

/// Converts to uppercase
pub fn upper(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.to_uppercase())
}

/// Alias for the `upper()` filter
pub fn uppercase(s: &fmt::Display) -> Result<String> {
    upper(s)
}

/// Strip leading and trailing whitespace
pub fn trim(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.trim().to_owned())
}

/// Limit string length, appends '...' if truncated
pub fn truncate(s: &fmt::Display, len: &usize) -> Result<String> {
    let mut s = s.to_string();
    if s.len() < *len {
        Ok(s)
    } else {
        s.truncate(*len);
        s.push_str("...");
        Ok(s)
    }
}

/// Indent lines with `width` spaces
pub fn indent(s: &fmt::Display, width: &usize) -> Result<String> {
    let s = s.to_string();

    let mut indented = String::new();

    for (i, c) in s.char_indices() {
        indented.push(c);

        if c == '\n' && i < s.len() - 1 {
            for _ in 0..*width {
                indented.push(' ');
            }
        }
    }

    Ok(indented)
}

/// Casts number to f64
pub fn into_f64<T>(number: T) -> Result<f64>
where
    T: NumCast,
{
    number.to_f64().ok_or(Fmt(fmt::Error))
}

/// Casts number to isize
pub fn into_isize<T>(number: T) -> Result<isize>
where
    T: NumCast,
{
    number.to_isize().ok_or(Fmt(fmt::Error))
}

/// Joins iterable into a string separated by provided argument
pub fn join<T, I, S>(input: I, separator: S) -> Result<String>
where
    T: fmt::Display,
    I: Iterator<Item = T>,
    S: AsRef<str>,
{
    let separator: &str = separator.as_ref();

    let mut rv = String::new();

    for (num, item) in input.enumerate() {
        if num > 0 {
            rv.push_str(separator);
        }

        rv.push_str(&format!("{}", item));
    }

    Ok(rv)
}

/// Absolute value
pub fn abs<T>(number: T) -> Result<T>
where
    T: Signed,
{
    Ok(number.abs())
}

/// Capitalize a value. The first character will be uppercase, all others lowercase.
pub fn capitalize(s: &fmt::Display) -> Result<String> {
    let mut s = s.to_string();

    match s.get_mut(0..1).map(|s| {
        s.make_ascii_uppercase();
        &*s
    }) {
        None => Ok(s),
        _ => match s.get_mut(1..).map(|s| {
            s.make_ascii_lowercase();
            &*s
        }) {
            _ => Ok(s),
        },
    }
}

/// Centers the value in a field of a given width
pub fn center(s: &fmt::Display, l: usize) -> Result<String> {
    let s = s.to_string();
    let len = s.len();

    if l <= len {
        Ok(s)
    } else {
        let p = l - len;
        let q = p / 2;
        let r = p % 2;
        let mut buf = String::with_capacity(l);

        for _ in 0..q {
            buf.push(' ');
        }

        buf.push_str(&s);

        for _ in 0..q + r {
            buf.push(' ');
        }

        Ok(buf)
    }
}

/// Count the words in that string
pub fn wordcount(s: &fmt::Display) -> Result<usize> {
    let s = s.to_string();

    Ok(s.split_whitespace().count())
}

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

    #[test]
    fn test_filesizeformat() {
        assert_eq!(filesizeformat(0).unwrap(), "0 B");
        assert_eq!(filesizeformat(999).unwrap(), "999 B");
        assert_eq!(filesizeformat(1000).unwrap(), "1 KB");
        assert_eq!(filesizeformat(1023).unwrap(), "1.02 KB");
        assert_eq!(filesizeformat(1024).unwrap(), "1.02 KB");
    }

    #[test]
    fn test_linebreaks() {
        assert_eq!(
            linebreaks(&"Foo\nBar Baz").unwrap(),
            "
Foo
Bar Baz
"
        );
        assert_eq!(
            linebreaks(&"Foo\nBar\n\nBaz").unwrap(),
            "
Foo
Bar

Baz
"
        );
    }

    #[test]
    fn test_linebreaksbr() {
        assert_eq!(linebreaksbr(&"Foo\nBar").unwrap(), "Foo
Bar");
        assert_eq!(
            linebreaksbr(&"Foo\nBar\n\nBaz").unwrap(),
            "Foo
Bar

Baz"
        );
    }

    #[test]
    fn test_lower() {
        assert_eq!(lower(&"Foo").unwrap(), "foo");
        assert_eq!(lower(&"FOO").unwrap(), "foo");
        assert_eq!(lower(&"FooBar").unwrap(), "foobar");
        assert_eq!(lower(&"foo").unwrap(), "foo");
    }

    #[test]
    fn test_upper() {
        assert_eq!(upper(&"Foo").unwrap(), "FOO");
        assert_eq!(upper(&"FOO").unwrap(), "FOO");
        assert_eq!(upper(&"FooBar").unwrap(), "FOOBAR");
        assert_eq!(upper(&"foo").unwrap(), "FOO");
    }

    #[test]
    fn test_trim() {
        assert_eq!(trim(&" Hello\tworld\t").unwrap(), "Hello\tworld");
    }

    #[test]
    fn test_indent() {
        assert_eq!(indent(&"hello", &2).unwrap(), "hello");
        assert_eq!(indent(&"hello\n", &2).unwrap(), "hello\n");
        assert_eq!(indent(&"hello\nfoo", &2).unwrap(), "hello\n  foo");
        assert_eq!(
            indent(&"hello\nfoo\n bar", &4).unwrap(),
            "hello\n    foo\n     bar"
        );
    }

    #[test]
    fn test_into_f64() {
        assert_eq!(into_f64(1).unwrap(), 1.0 as f64);
        assert_eq!(into_f64(1.9).unwrap(), 1.9 as f64);
        assert_eq!(into_f64(-1.9).unwrap(), -1.9 as f64);
        assert_eq!(into_f64(INFINITY as f32).unwrap(), INFINITY);
        assert_eq!(into_f64(-INFINITY as f32).unwrap(), -INFINITY);
    }

    #[test]
    fn test_into_isize() {
        assert_eq!(into_isize(1).unwrap(), 1 as isize);
        assert_eq!(into_isize(1.9).unwrap(), 1 as isize);
        assert_eq!(into_isize(-1.9).unwrap(), -1 as isize);
        assert_eq!(into_isize(1.5 as f64).unwrap(), 1 as isize);
        assert_eq!(into_isize(-1.5 as f64).unwrap(), -1 as isize);
        match into_isize(INFINITY) {
            Err(Fmt(fmt::Error)) => assert!(true),
            _ => assert!(false, "Should return error of type Err(Fmt(fmt::Error))"),
        };
    }

    #[test]
    fn test_join() {
        assert_eq!(
            join((&["hello", "world"]).into_iter(), ", ").unwrap(),
            "hello, world"
        );
        assert_eq!(join((&["hello"]).into_iter(), ", ").unwrap(), "hello");

        let empty: &[&str] = &[];
        assert_eq!(join(empty.into_iter(), ", ").unwrap(), "");

        let input: Vec<String> = vec!["foo".into(), "bar".into(), "bazz".into()];
        assert_eq!(
            join((&input).into_iter(), ":".to_string()).unwrap(),
            "foo:bar:bazz"
        );
        assert_eq!(
            join(input.clone().into_iter(), ":").unwrap(),
            "foo:bar:bazz"
        );
        assert_eq!(
            join(input.clone().into_iter(), ":".to_string()).unwrap(),
            "foo:bar:bazz"
        );

        let input: &[String] = &["foo".into(), "bar".into()];
        assert_eq!(join(input.into_iter(), ":").unwrap(), "foo:bar");
        assert_eq!(join(input.into_iter(), ":".to_string()).unwrap(), "foo:bar");

        let real: String = "blah".into();
        let input: Vec<&str> = vec![&real];
        assert_eq!(join(input.into_iter(), ";").unwrap(), "blah");

        assert_eq!(
            join((&&&&&["foo", "bar"]).into_iter(), ", ").unwrap(),
            "foo, bar"
        );
    }

    #[test]
    fn test_abs() {
        assert_eq!(abs(1).unwrap(), 1);
        assert_eq!(abs(-1).unwrap(), 1);
        assert_eq!(abs(1.0).unwrap(), 1.0);
        assert_eq!(abs(-1.0).unwrap(), 1.0);
        assert_eq!(abs(1.0 as f64).unwrap(), 1.0 as f64);
        assert_eq!(abs(-1.0 as f64).unwrap(), 1.0 as f64);
    }

    #[test]
    fn test_capitalize() {
        assert_eq!(capitalize(&"foo").unwrap(), "Foo".to_string());
        assert_eq!(capitalize(&"f").unwrap(), "F".to_string());
        assert_eq!(capitalize(&"fO").unwrap(), "Fo".to_string());
        assert_eq!(capitalize(&"").unwrap(), "".to_string());
        assert_eq!(capitalize(&"FoO").unwrap(), "Foo".to_string());
        assert_eq!(capitalize(&"foO BAR").unwrap(), "Foo bar".to_string());
    }

    #[test]
    fn test_center() {
        assert_eq!(center(&"f", 3).unwrap(), " f ".to_string());
        assert_eq!(center(&"f", 4).unwrap(), " f  ".to_string());
        assert_eq!(center(&"foo", 1).unwrap(), "foo".to_string());
        assert_eq!(center(&"foo bar", 8).unwrap(), "foo bar ".to_string());
    }

    #[test]
    fn test_wordcount() {
        assert_eq!(wordcount(&"").unwrap(), 0);
        assert_eq!(wordcount(&" \n\t").unwrap(), 0);
        assert_eq!(wordcount(&"foo").unwrap(), 1);
        assert_eq!(wordcount(&"foo bar").unwrap(), 2);
    }
}
<|endoftext|>


<commit_before>
use std::collections::BTreeMap;
use std::mem;

use assembler::data_range::DataRange;
use assembler::directive::Directive;
use assembler::instruction::Instruction;
use assembler::parser::Node;
use assembler::source_file_location::SourceFileLocation;
use assembler::u12::*;

// MARK: - Constants

lazy_static! {
  static ref BYTES_PER_INSTRUCTION: U12 = U12::from(2);
}

// MARK: - Helper Methods

fn format_semantic_error<'a>(location: &'a SourceFileLocation, description: String) -> String {
  format!("{}: error: {}", location, description)
}

// MARK: - Pass 1: Define Labels

/**
 In a 2-pass assembler, the first pass is used to resolve the address of all labels
 declared in the assembly. This involves walking the ASL, computing the size of instructions
 and directives in the output stream and using that information to establish the label-address map.
 @param syntax_list The un-filtered syntax list.
 @return The map of labels to their corresponding addresses on success or a string describing
     the reason the first pass failed.
 */
fn define_labels<'a>(syntax_list: &Vec<Node<'a>>) -> Result<BTreeMap<&'a str, U12>, String> {
  let mut label_address_map = BTreeMap::new();
  let mut current_address = U12::zero();

  // Define all labels and process '.org' directives.
  for node in syntax_list.iter() {
    match *node {
      Node::Directive(ref data) => {
        // Validate the directive and that the arguments match the identifier.
        let directive = match Directive::from_identifier_and_parameters(data.identifier, &data.arguments) {
           Ok(result) => result,
          Err(reason) => return Err(format_semantic_error(&data.location, reason))
        };

        // Ensure the directive is not too large to emit.
        let directive_size = match directive.size().as_u12() {
          Some(size) => size,
          None => {
            let reason = format!("Directive ({}) size {} exceeds 4096 bytes.", directive, directive.size());
            return Err(format_semantic_error(&data.location, reason));
          }
        };

        // Offset the current address by the size of the directive being processed.
        current_address = match current_address.checked_add(directive_size) {
          Some(sum) => sum,
          None => {
            let reason = format!("Directive ({}) would cause the 12-bit address counter to overflow.", directive);
            return Err(format_semantic_error(&data.location, reason));
          }
        };

        // The origin directive changes the current address.
        if let Directive::Org(address) = directive {
          current_address = address;
        }
      }

      Node::Label(ref data) => {
        // Map the label to the current address and remove from the ASL.
        if label_address_map.contains_key(&data.identifier) {
          let reason = format!("Attempted re-definition of label {}.", data.identifier);
          return Err(format_semantic_error(&data.location, reason));
        } else {
          label_address_map.insert(data.identifier, current_address);
        }
      }

      Node::Instruction(ref data) => {
        // All Chip8 instructions are the same length.
        current_address = match current_address.checked_add(*BYTES_PER_INSTRUCTION) {
          Some(sum) => sum,
          None => {
            let reason = format!("Instruction would cause address counter to overflow $FFF.");
            return Err(format_semantic_error(&data.location, reason));
          }
        };
      }
    }
  }

  // Semantic analysis for directives and labels is complete.
  Ok(label_address_map)
}

// MARK: - Pass 2: Emit Bytes

fn emit_data_ranges<'a>(syntax_list: Vec<Node<'a>>, label_address_map: &BTreeMap<&'a str, U12>) -> Result<Vec<DataRange>, String> {
  let mut data_ranges = Vec::new();
  let mut current_range = DataRange::new(U12::zero());

  for node in syntax_list {
    match node {

      Node::Directive(data) => {
        match Directive::from_identifier_and_parameters(data.identifier, &data.arguments).unwrap() {

          // Process the origin directive to build a new data range.
          Directive::Org(address) => {
            let new_range = DataRange::new(address);
            let previous_range = mem::replace(&mut current_range, new_range);
            if previous_range.len() > 0 {
              data_ranges.push(previous_range);
            }
          }

          // Emit the directive into the range.
          directive @ _ => {
            assert_eq!(current_range.append(directive.into_vec().as_slice()), true);
          }

        }
      }

      Node::Instruction(data) => {
        // Verify the semantics and append the instruction to the output buffer.
        match Instruction::from_mnemonic_and_parameters(data.mnemonic, &data.fields, label_address_map) {
          Ok(instruction) => {
            assert_eq!(current_range.append(instruction.into_vec().as_slice()), true);
          }
          Err(reason) => {
            return Err(format_semantic_error(&data.location, reason));
          }
        }
      }

      Node::Label(_) => {
        // Processed in Pass 1.
      }

    }
  }

  // Push the last range into the list.
  data_ranges.push(current_range);
  Ok(data_ranges)
}

// MARK: - Public API

/**
 Analyze the ASL for the assembly and convert it into an output byte stream.
 */
pub fn assemble<'a>(syntax_list: Vec<Node<'a>>) -> Result<Vec<DataRange>, String> {
  let label_address_map = try!(define_labels(&syntax_list));
  emit_data_ranges(syntax_list, &label_address_map)
}

// MARK: - Tests

// #[cfg(test)]
// mod tests {

//   use assembler::parser::*;
//   use assembler::source_file_location::SourceFileLocation;

//   use super::{validate_directive_semantics, size_of_directive};
//   use super::define_labels;

//   // MARK: - Helpers

//   /// Creates a Label type node with associated data at location "-:seq:1-name.len()".
//   fn make_label_node<'a>(seq: usize, name: &'a str) -> Node<'a> {
//     Node::Label (
//       LabelData {
//         location: SourceFileLocation::new("-", seq, 1, name.len()),
//         identifier: name
//       }
//     )
//   }

//   /// Creates a Directive type node with associated data at location "-:seq:2-name.len()+1".
//   fn make_directive_node<'a>(seq: usize, name: &'a str, args: Vec<Literal<'a>>) -> Node<'a> {
//     Node::Directive (
//       DirectiveData {
//         location: SourceFileLocation::new("-", seq, 2, name.len()),
//         identifier: name,
//         arguments: args
//       }
//     )
//   }

//   /// Creates an Instruction type node with associated data at location "-:seq:1-name.len()".
//   fn make_instruction_node<'a>(seq: usize, name: &'a str, fields: Vec<InstructionField<'a>>) -> Node<'a> {
//     Node::Instruction (
//       InstructionData {
//         location: SourceFileLocation::new("-", seq, 1, name.len()),
//         mnemonic: name,
//         fields: fields
//       }
//     )
//   }

//   // MARK: - Pass 1 Tests

//   #[test]
//   fn test_define_labels() {
//     let program = vec![
//       make_directive_node   (1, "org", vec![Literal::Numeric(0x100)]),
//       make_label_node       (2, "label1"),
//       make_directive_node   (3, "db", vec![Literal::Numeric(0xFF)]),
//       make_label_node       (4, "label2"),
//       make_instruction_node (5, "trap", vec![]),
//       make_label_node       (6, "label3"),
//     ];

//     let result = define_labels(&program);

//     // Assert that semantic analysis passed.
//     if let Err(reason) = result {
//       assert!(false, "Unexpected failure in assembler pass 1: {}", reason);
//       return;
//     }

//     // Verify that the
//     if let Ok(label_map) = result {
//       assert_eq!(label_map.len(), 3);
//       assert_eq!(label_map.get("label1").unwrap(), &0x100);
//       assert_eq!(label_map.get("label2").unwrap(), &0x101);
//       assert_eq!(label_map.get("label3").unwrap(), &0x103);
//     }
//   }

//   #[test]
//   fn test_define_labels_does_semantic_analysis_on_org() {
//     let program = vec![
//       make_directive_node(1, "org", vec![])
//     ];

//     let result = define_labels(&program);
//     assert_eq!(result, Err("Incorrect number of parameters (0) for directive .org, expecting 1.".to_string()));
//   }

//   #[test]
//   fn test_define_labels_does_semantic_analysis_on_db() {
//     let program = vec![
//       make_directive_node(1, "db", vec![])
//     ];

//     let result = define_labels(&program);
//     assert_eq!(result, Err("Incorrect number of parameters (0) for directive .db, expecting 1 or more.".to_string()));
//   }

//   #[test]
//   fn test_define_labels_fails_on_redefinition() {
//     let program = vec![
//       make_label_node(1, "L1"),
//       make_label_node(2, "L1"),
//     ];

//     let result = define_labels(&program);
//     assert_eq!(result, Err("Attempted re-definition of label L1.".to_string()));
//   }

//   // MARK: - Pass 2 Tests

// }

<commit_msg>Tests for Pass 1 errors.<commit_after>
use std::collections::BTreeMap;
use std::mem;

use assembler::data_range::DataRange;
use assembler::directive::Directive;
use assembler::instruction::Instruction;
use assembler::parser::Node;
use assembler::source_file_location::SourceFileLocation;
use assembler::u12::*;

// MARK: - Constants

lazy_static! {
  static ref BYTES_PER_INSTRUCTION: U12 = U12::from(2);
}

// MARK: - Helper Methods

fn format_semantic_error<'a>(location: &'a SourceFileLocation, description: String) -> String {
  format!("{}: error: {}", location, description)
}

// MARK: - Pass 1: Define Labels

/**
 In a 2-pass assembler, the first pass is used to resolve the address of all labels
 declared in the assembly. This involves walking the ASL, computing the size of instructions
 and directives in the output stream and using that information to establish the label-address map.
 @param syntax_list The un-filtered syntax list.
 @return The map of labels to their corresponding addresses on success or a string describing
     the reason the first pass failed.
 */
fn define_labels<'a>(syntax_list: &Vec<Node<'a>>) -> Result<BTreeMap<&'a str, U12>, String> {
  let mut label_address_map = BTreeMap::new();
  let mut current_address = U12::zero();

  // Define all labels and process '.org' directives.
  for node in syntax_list.iter() {
    match *node {
      Node::Directive(ref data) => {
        // Validate the directive and that the arguments match the identifier.
        let directive = match Directive::from_identifier_and_parameters(data.identifier, &data.arguments) {
           Ok(result) => result,
          Err(reason) => return Err(format_semantic_error(&data.location, reason))
        };

        // Ensure the directive is not too large to emit.
        let directive_size = match directive.size().as_u12() {
          Some(size) => size,
          None => {
            let reason = format!("Directive ({}) size {} exceeds 4096 bytes.", directive, directive.size());
            return Err(format_semantic_error(&data.location, reason));
          }
        };

        // Offset the current address by the size of the directive being processed.
        current_address = match current_address.checked_add(directive_size) {
          Some(sum) => sum,
          None => {
            let reason = format!("Directive ({}) would cause the 12-bit address counter to overflow.", directive);
            return Err(format_semantic_error(&data.location, reason));
          }
        };

        // The origin directive changes the current address.
        if let Directive::Org(address) = directive {
          current_address = address;
        }
      }

      Node::Label(ref data) => {
        // Map the label to the current address and remove from the ASL.
        if label_address_map.contains_key(&data.identifier) {
          let reason = format!("Attempted re-definition of label {}.", data.identifier);
          return Err(format_semantic_error(&data.location, reason));
        } else {
          label_address_map.insert(data.identifier, current_address);
        }
      }

      Node::Instruction(ref data) => {
        // All Chip8 instructions are the same length.
        current_address = match current_address.checked_add(*BYTES_PER_INSTRUCTION) {
          Some(sum) => sum,
          None => {
            let reason = format!("Instruction would cause address counter to overflow $FFF.");
            return Err(format_semantic_error(&data.location, reason));
          }
        };
      }
    }
  }

  // Semantic analysis for directives and labels is complete.
  Ok(label_address_map)
}

// MARK: - Pass 2: Emit Bytes

fn emit_data_ranges<'a>(syntax_list: Vec<Node<'a>>, label_address_map: &BTreeMap<&'a str, U12>) -> Result<Vec<DataRange>, String> {
  let mut data_ranges = Vec::new();
  let mut current_range = DataRange::new(U12::zero());

  for node in syntax_list {
    match node {

      Node::Directive(data) => {
        match Directive::from_identifier_and_parameters(data.identifier, &data.arguments).unwrap() {

          // Process the origin directive to build a new data range.
          Directive::Org(address) => {
            let new_range = DataRange::new(address);
            let previous_range = mem::replace(&mut current_range, new_range);
            if previous_range.len() > 0 {
              data_ranges.push(previous_range);
            }
          }

          // Emit the directive into the range.
          directive @ _ => {
            assert_eq!(current_range.append(directive.into_vec().as_slice()), true);
          }

        }
      }

      Node::Instruction(data) => {
        // Verify the semantics and append the instruction to the output buffer.
        match Instruction::from_mnemonic_and_parameters(data.mnemonic, &data.fields, label_address_map) {
          Ok(instruction) => {
            assert_eq!(current_range.append(instruction.into_vec().as_slice()), true);
          }
          Err(reason) => {
            return Err(format_semantic_error(&data.location, reason));
          }
        }
      }

      Node::Label(_) => {
        // Processed in Pass 1.
      }

    }
  }

  // Push the last range into the list.
  data_ranges.push(current_range);
  Ok(data_ranges)
}

// MARK: - Public API

/**
 Analyze the ASL for the assembly and convert it into an output byte stream.
 */
pub fn assemble<'a>(syntax_list: Vec<Node<'a>>) -> Result<Vec<DataRange>, String> {
  let label_address_map = try!(define_labels(&syntax_list));
  emit_data_ranges(syntax_list, &label_address_map)
}

// MARK: - Tests

#[cfg(test)]
mod tests {

  use assembler::parser::*;
  use assembler::source_file_location::SourceFileLocation;
  use assembler::u12::*;

  use super::define_labels;

  // MARK: - Helpers

  /// Creates a Label type node with associated data at location "-:seq:1-name.len()".
  fn make_label_node<'a>(seq: usize, name: &'a str) -> Node<'a> {
    Node::Label (
      LabelData {
        location: SourceFileLocation::new("-", seq, 1, name.len()),
        identifier: name
      }
    )
  }

  /// Creates a Directive type node with associated data at location "-:seq:2-name.len()+1".
  fn make_directive_node<'a>(seq: usize, name: &'a str, args: Vec<Literal<'a>>) -> Node<'a> {
    Node::Directive (
      DirectiveData {
        location: SourceFileLocation::new("-", seq, 2, name.len()),
        identifier: name,
        arguments: args
      }
    )
  }

  /// Creates an Instruction type node with associated data at location "-:seq:1-name.len()".
  fn make_instruction_node<'a>(seq: usize, name: &'a str, fields: Vec<InstructionField<'a>>) -> Node<'a> {
    Node::Instruction (
      InstructionData {
        location: SourceFileLocation::new("-", seq, 1, name.len()),
        mnemonic: name,
        fields: fields
      }
    )
  }

  // MARK: - Pass 1 Tests

  #[test]
  fn test_define_labels() {
    let program = vec![
      make_directive_node   (1, "org", vec![Literal::Numeric(0x100)]),
      make_label_node       (2, "label1"),
      make_directive_node   (3, "db", vec![Literal::Numeric(0xFF)]),
      make_label_node       (4, "label2"),
      make_instruction_node (5, "trap", vec![]),
      make_label_node       (6, "label3"),
    ];

    let result = define_labels(&program);

    // Assert that semantic analysis passed.
    if let Err(reason) = result {
      assert!(false, "Unexpected failure in assembler pass 1: {}", reason);
      return;
    }

    // Verify that the
    if let Ok(label_map) = result {
      assert_eq!(label_map.len(), 3);
      assert_eq!(label_map.get("label1").unwrap(), &(0x100.as_u12().unwrap()));
      assert_eq!(label_map.get("label2").unwrap(), &(0x101.as_u12().unwrap()));
      assert_eq!(label_map.get("label3").unwrap(), &(0x103.as_u12().unwrap()));
    }
  }

  #[test]
  fn test_define_labels_does_semantic_analysis_on_org() {
    let program = vec![
      make_directive_node(1, "org", vec![])
    ];

    let result = define_labels(&program);
    assert_eq!(result, Err(String::from("-:1:2-4: error: Incorrect number of parameters (0) for directive .org, expecting 1.")));
  }

  #[test]
  fn test_define_labels_does_semantic_analysis_on_db() {
    let program = vec![
      make_directive_node(1, "db", vec![])
    ];

    let result = define_labels(&program);
    assert_eq!(result, Err(String::from("-:1:2-3: error: Incorrect number of parameters (0) for directive .db, expecting 1 or more.")));
  }

  #[test]
  fn test_define_labels_fails_on_redefinition() {
    let program = vec![
      make_label_node(1, "L1"),
      make_label_node(2, "L1"),
    ];

    let result = define_labels(&program);
    assert_eq!(result, Err(String::from("-:2:1-2: error: Attempted re-definition of label L1.")));
  }

}

<|endoftext|>


<commit_before>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

use std::fmt;

static LATIN1_LCASE_TABLE: [u8; 256] = [
    // 0x00-0x0f
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    // 0x10-0x1f
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    // 0x20-0x2f
    b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'',
    b'(', b')', b'*', b'+', b',', b'-', b'.', b'/',
    // 0x30-0x3f
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
    b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
    // 0x40-0x4f
    b'@', b'a', b'b', b'c', b'd', b'e', b'f', b'g',
    b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
    // 0x50-0x5f
    b'p', b'q', b'r', b's', b't', b'u', b'v', b'w',
    b'x', b'y', b'z', b'[', b'\\',b']', b'^', b'_',
    // 0x60-0x6f
    b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g',
    b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
    // 0x70-0x7f
    b'p', b'q', b'r', b's', b't', b'u', b'v', b'w',
    b'x', b'y', b'z', b'{', b'|', b'}', b'~', 0x7f,
    // 0x80-0x8f
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    // 0x90-0x9f
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    // 0xa0-0xaf
    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    // 0xb0-0xbf
    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    // 0xc0-0xcf
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    // 0xd0-0xdf
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7,
    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf,
    // 0xe0-0xef
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    // 0xf0-0xff
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];

// Ensure this table is UTF-8!
static LATIN1_PRETTYPRINT_TABLE: [&str; 256] = [
    // 0x00-0x0f
    "␀",     "␁",     "␂",     "␃",     "␄",     "␅",     "␆",     "␇",
    "␈",     "␉",     "␊",     "␋",     "␌",     "␍",     "␎",     "␏",
    // 0x10-0x1f
    "␐",     "␑",     "␒",     "␓",     "␔",     "␕",     "␖",     "␗",
    "␘",     "␙",     "␚",     "␛",     "␜",     "␝",     "␞",     "␟",
    // 0x20-0x2f
    " ",     "!",     "\"",    "#",     "$",     "%",     "&",     "'",
    "(",     ")",     "*",     "+",     ",",     "-",     ".",     "/",
    // 0x30-0x3f
    "0",     "1",     "2",     "3",     "4",     "5",     "6",     "7",
    "8",     "9",     ":",     ";",     "<",     "=",     ">",     "?",
    // 0x40-0x4f
    "@",     "A",     "B",     "C",     "D",     "E",     "F",     "G",
    "H",     "I",     "J",     "K",     "L",     "M",     "N",     "O",
    // 0x50-0x5f
    "P",     "Q",     "R",     "S",     "T",     "U",     "V",     "W",
    "X",     "Y",     "Z",     "[",     "\\",    "]",     "^",     "_",
    // 0x60-0x6f
    "`",     "a",     "b",     "c",     "d",     "e",     "f",     "g",
    "h",     "i",     "j",     "k",     "l",     "m",     "n",     "o",
    // 0x70-0x7f
    "p",     "q",     "r",     "s",     "t",     "u",     "v",     "w",
    "x",     "y",     "z",     "{",     "|",     "}",     "~",     "␡",
    // 0x80-0x8f
    "\\x80", "\\x81", "\\x82", "\\x83", "\\x84", "\\x85", "\\x86", "\\x87",
    "\\x88", "\\x89", "\\x8a", "\\x8b", "\\x8c", "\\x8d", "\\x8e", "\\x8f",
    // 0x90-0x9f
    "\\x90", "\\x91", "\\x92", "\\x93", "\\x94", "\\x95", "\\x96", "\\x97",
    "\\x98", "\\x99", "\\x9a", "\\x9b", "\\x9c", "\\x9d", "\\x9e", "\\x9f",
    // 0xa0-0xaf
    "\\xa0", "¡",     "¢",     "£",     "¤",     "¥",     "¦",     "§",
    "¨",     "©",     "ª",     "«",     "¬",     "\\xad", "®",     "¯",
    // 0xb0-0xbf
    "°",     "±",     "²",     "³",     "´",     "µ",     "¶",     "·",
    "¸",     "¹",     "º",     "»",     "¼",     "½",     "¾",     "¿",
    // 0xc0-0xcf
    "À",     "Á",     "Â",     "Ã",     "Ä",     "Å",     "Æ",     "Ç",
    "È",     "É",     "Ê",     "Ë",     "Ì",     "Í",     "Î",     "Ï",
    // 0xd0-0xdf
    "Ð",     "Ñ",     "Ò",     "Ó",     "Ô",     "Õ",     "Ö",     "×",
    "Ø",     "Ù",     "Ú",     "Û",     "Ü",     "Ý",     "Þ",     "ß",
    // 0xe0-0xef
    "à",     "á",     "â",     "ã",     "ä",     "å",     "æ",     "ç",
    "è",     "é",     "ê",     "ë",     "ì",     "í",     "î",     "ï",
    // 0xf0-0xff
    "ð",     "ñ",     "ò",     "ó",     "ô",     "õ",     "ö",     "÷",
    "ø",     "ù",     "ú",     "û",     "ü",     "ý",     "þ",     "ÿ",
];

fn char_valid_for_basic_id(c: u8) -> bool {
    match c {
        // Upper-case letters
        0x41...0x5a => true,
        0xc0...0xd6 => true,
        0xd8...0xde => true,
        // Lower-case letters
        0x61...0x7a => true,
        0xdf...0xf6 => true,
        0xf8...0xff => true,
        // Underscore
        0x5f => true,
        // Digits
        0x30...0x39 => true,
        _ => false
    }
}

fn char_valid_for_ext_id(c: u8) -> bool {
    match c {
        // Cannot be C0 controls
        0x00...0x1f => false,
        // Cannot be C1 controls
        0x80...0x9f => false,
        // Cannot be delete
        0x7f => false,
        _ => true
    }
}

struct Latin1Str<'a> {
    bytes: &'a [u8],
}

impl<'a> Latin1Str<'a> {
    pub fn pretty_name(&self) -> String {
        let mut utf8str = String::new();
        for c in self.bytes {
            utf8str.push_str(LATIN1_PRETTYPRINT_TABLE[*c as usize]);
        }
        utf8str
    }

    pub fn valid_for_basic_id(&self) -> bool {
        // Needs to be at least one character long
        if self.bytes.len() == 0 {
            return false;
        }

        // Validate all characters
        for c in self.bytes {
            if !(char_valid_for_basic_id(*c)) {
                return false;
            }
        }

        // First character cannot be a digit or underscore
        if !(match self.bytes[0] {
            0x30...0x39 => false,
            0x5f => false,
            _ => true
        }) {
            return false;
        }

        // Cannot have consecutive underscores
        for i in 1..self.bytes.len() {
            if self.bytes[i - 1] == 0x5f && self.bytes[i] == 0x5f {
                return false;
            }
        }

        // Cannot end with an underscore
        if self.bytes[self.bytes.len() - 1] == 0x5f {
            return false;
        }

        true
    }

    pub fn valid_for_ext_id(&self) -> bool {
        // Needs to be at least one character long
        if self.bytes.len() == 0 {
            return false;
        }

        // Validate all characters
        for c in self.bytes {
            if !(char_valid_for_ext_id(*c)) {
                return false;
            }
        }

        true
    }
}

impl<'a> fmt::Display for Latin1Str<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.pretty_name())
    }
}

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

    #[test]
    fn latin1_pretty_name() {
        let x = Latin1Str {bytes: b"test"};
        assert_eq!(x.pretty_name(), "test");
        let x = Latin1Str {bytes: b"te\xC0st\xFE"};
        assert_eq!(x.pretty_name(), "teÀstþ");
    }

    #[test]
    fn basic_id_validator() {
        assert!((Latin1Str {bytes: b"foo"}).valid_for_basic_id());
        assert!((Latin1Str {bytes: b"foo012"}).valid_for_basic_id());
        assert!((Latin1Str {bytes: b"foo_012"}).valid_for_basic_id());
        assert!(!(Latin1Str {bytes: b"f__oo"}).valid_for_basic_id());
        assert!(!(Latin1Str {bytes: b"foo_"}).valid_for_basic_id());
        assert!(!(Latin1Str {bytes: b"_foo"}).valid_for_basic_id());
    }
}
<commit_msg>Add new and raw_name methods to latin1str<commit_after>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

use std::fmt;

static LATIN1_LCASE_TABLE: [u8; 256] = [
    // 0x00-0x0f
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    // 0x10-0x1f
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    // 0x20-0x2f
    b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'',
    b'(', b')', b'*', b'+', b',', b'-', b'.', b'/',
    // 0x30-0x3f
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
    b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
    // 0x40-0x4f
    b'@', b'a', b'b', b'c', b'd', b'e', b'f', b'g',
    b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
    // 0x50-0x5f
    b'p', b'q', b'r', b's', b't', b'u', b'v', b'w',
    b'x', b'y', b'z', b'[', b'\\',b']', b'^', b'_',
    // 0x60-0x6f
    b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g',
    b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
    // 0x70-0x7f
    b'p', b'q', b'r', b's', b't', b'u', b'v', b'w',
    b'x', b'y', b'z', b'{', b'|', b'}', b'~', 0x7f,
    // 0x80-0x8f
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    // 0x90-0x9f
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    // 0xa0-0xaf
    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    // 0xb0-0xbf
    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    // 0xc0-0xcf
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    // 0xd0-0xdf
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7,
    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf,
    // 0xe0-0xef
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    // 0xf0-0xff
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];

// Ensure this table is UTF-8!
static LATIN1_PRETTYPRINT_TABLE: [&str; 256] = [
    // 0x00-0x0f
    "␀",     "␁",     "␂",     "␃",     "␄",     "␅",     "␆",     "␇",
    "␈",     "␉",     "␊",     "␋",     "␌",     "␍",     "␎",     "␏",
    // 0x10-0x1f
    "␐",     "␑",     "␒",     "␓",     "␔",     "␕",     "␖",     "␗",
    "␘",     "␙",     "␚",     "␛",     "␜",     "␝",     "␞",     "␟",
    // 0x20-0x2f
    " ",     "!",     "\"",    "#",     "$",     "%",     "&",     "'",
    "(",     ")",     "*",     "+",     ",",     "-",     ".",     "/",
    // 0x30-0x3f
    "0",     "1",     "2",     "3",     "4",     "5",     "6",     "7",
    "8",     "9",     ":",     ";",     "<",     "=",     ">",     "?",
    // 0x40-0x4f
    "@",     "A",     "B",     "C",     "D",     "E",     "F",     "G",
    "H",     "I",     "J",     "K",     "L",     "M",     "N",     "O",
    // 0x50-0x5f
    "P",     "Q",     "R",     "S",     "T",     "U",     "V",     "W",
    "X",     "Y",     "Z",     "[",     "\\",    "]",     "^",     "_",
    // 0x60-0x6f
    "`",     "a",     "b",     "c",     "d",     "e",     "f",     "g",
    "h",     "i",     "j",     "k",     "l",     "m",     "n",     "o",
    // 0x70-0x7f
    "p",     "q",     "r",     "s",     "t",     "u",     "v",     "w",
    "x",     "y",     "z",     "{",     "|",     "}",     "~",     "␡",
    // 0x80-0x8f
    "\\x80", "\\x81", "\\x82", "\\x83", "\\x84", "\\x85", "\\x86", "\\x87",
    "\\x88", "\\x89", "\\x8a", "\\x8b", "\\x8c", "\\x8d", "\\x8e", "\\x8f",
    // 0x90-0x9f
    "\\x90", "\\x91", "\\x92", "\\x93", "\\x94", "\\x95", "\\x96", "\\x97",
    "\\x98", "\\x99", "\\x9a", "\\x9b", "\\x9c", "\\x9d", "\\x9e", "\\x9f",
    // 0xa0-0xaf
    "\\xa0", "¡",     "¢",     "£",     "¤",     "¥",     "¦",     "§",
    "¨",     "©",     "ª",     "«",     "¬",     "\\xad", "®",     "¯",
    // 0xb0-0xbf
    "°",     "±",     "²",     "³",     "´",     "µ",     "¶",     "·",
    "¸",     "¹",     "º",     "»",     "¼",     "½",     "¾",     "¿",
    // 0xc0-0xcf
    "À",     "Á",     "Â",     "Ã",     "Ä",     "Å",     "Æ",     "Ç",
    "È",     "É",     "Ê",     "Ë",     "Ì",     "Í",     "Î",     "Ï",
    // 0xd0-0xdf
    "Ð",     "Ñ",     "Ò",     "Ó",     "Ô",     "Õ",     "Ö",     "×",
    "Ø",     "Ù",     "Ú",     "Û",     "Ü",     "Ý",     "Þ",     "ß",
    // 0xe0-0xef
    "à",     "á",     "â",     "ã",     "ä",     "å",     "æ",     "ç",
    "è",     "é",     "ê",     "ë",     "ì",     "í",     "î",     "ï",
    // 0xf0-0xff
    "ð",     "ñ",     "ò",     "ó",     "ô",     "õ",     "ö",     "÷",
    "ø",     "ù",     "ú",     "û",     "ü",     "ý",     "þ",     "ÿ",
];

fn char_valid_for_basic_id(c: u8) -> bool {
    match c {
        // Upper-case letters
        0x41...0x5a => true,
        0xc0...0xd6 => true,
        0xd8...0xde => true,
        // Lower-case letters
        0x61...0x7a => true,
        0xdf...0xf6 => true,
        0xf8...0xff => true,
        // Underscore
        0x5f => true,
        // Digits
        0x30...0x39 => true,
        _ => false
    }
}

fn char_valid_for_ext_id(c: u8) -> bool {
    match c {
        // Cannot be C0 controls
        0x00...0x1f => false,
        // Cannot be C1 controls
        0x80...0x9f => false,
        // Cannot be delete
        0x7f => false,
        _ => true
    }
}

pub struct Latin1Str<'a> {
    bytes: &'a [u8],
}

impl<'a> Latin1Str<'a> {
    pub fn new(inp: &[u8]) -> Latin1Str {
        Latin1Str {bytes: inp}
    }

    pub fn raw_name(&self) -> &[u8] {
        self.bytes
    }

    pub fn pretty_name(&self) -> String {
        let mut utf8str = String::new();
        for c in self.bytes {
            utf8str.push_str(LATIN1_PRETTYPRINT_TABLE[*c as usize]);
        }
        utf8str
    }

    pub fn valid_for_basic_id(&self) -> bool {
        // Needs to be at least one character long
        if self.bytes.len() == 0 {
            return false;
        }

        // Validate all characters
        for c in self.bytes {
            if !(char_valid_for_basic_id(*c)) {
                return false;
            }
        }

        // First character cannot be a digit or underscore
        if !(match self.bytes[0] {
            0x30...0x39 => false,
            0x5f => false,
            _ => true
        }) {
            return false;
        }

        // Cannot have consecutive underscores
        for i in 1..self.bytes.len() {
            if self.bytes[i - 1] == 0x5f && self.bytes[i] == 0x5f {
                return false;
            }
        }

        // Cannot end with an underscore
        if self.bytes[self.bytes.len() - 1] == 0x5f {
            return false;
        }

        true
    }

    pub fn valid_for_ext_id(&self) -> bool {
        // Needs to be at least one character long
        if self.bytes.len() == 0 {
            return false;
        }

        // Validate all characters
        for c in self.bytes {
            if !(char_valid_for_ext_id(*c)) {
                return false;
            }
        }

        true
    }
}

impl<'a> fmt::Display for Latin1Str<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.pretty_name())
    }
}

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

    #[test]
    fn latin1_pretty_name() {
        let x = Latin1Str::new(b"test");
        assert_eq!(x.pretty_name(), "test");
        assert_eq!(x.raw_name(), b"test");
        let x = Latin1Str::new(b"te\xC0st\xFE");
        assert_eq!(x.raw_name(), b"te\xC0st\xFE");
        assert_eq!(x.pretty_name(), "teÀstþ");
    }

    #[test]
    fn basic_id_validator() {
        assert!((Latin1Str::new(b"foo").valid_for_basic_id()));
        assert!((Latin1Str::new(b"foo012").valid_for_basic_id()));
        assert!((Latin1Str::new(b"foo_012").valid_for_basic_id()));
        assert!(!(Latin1Str::new(b"f__oo").valid_for_basic_id()));
        assert!(!(Latin1Str::new(b"foo_").valid_for_basic_id()));
        assert!(!(Latin1Str::new(b"_foo").valid_for_basic_id()));
    }
}
<|endoftext|>


<commit_before>use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use regex::Regex;

pub mod id;
pub mod id_type;
pub mod header;
pub mod hash;

use storage::file::id::*;
use self::header::data::*;

/**
 * Internal abstract view on a file. Does not neccessarily exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File {
    /// The name of the module which owns this file
    pub owning_module_name  : &'static str,

    /// The header of the file
    pub header              : FileHeaderData,

    /// The content part of the file
    pub data                : String,

    /// The ID of the file
    pub id                  : FileID,
}

impl File {

    /**
     * Get the owner module name of the file
     */
    pub fn owner_name(&self) -> &'static str {
        self.owning_module_name
    }

    /**
     * Get the header of the file
     */
    pub fn header(&self) -> &FileHeaderData {
        &self.header
    }

    /**
     * Set the header of the file
     */
    pub fn set_header(&mut self, new_header: FileHeaderData) {
        self.header = new_header;
    }

    /**
     * Set the data of the file
     */
    pub fn data(&self) -> &String {
        &self.data
    }

    /**
     * Set the data
     */
    pub fn set_data(&mut self, new_data: String) {
        self.data = new_data;
    }

    /**
     * Set the (header, data) of the file
     */
    pub fn contents(&self) -> (&FileHeaderData, &String) {
        (self.header(), self.data())
    }

    /**
     * Set the id of the file
     */
    pub fn id(&self) -> &FileID {
        &self.id
    }

    /**
     * Check whether the header or the data of the file match some regex
     */
    pub fn matches_with(&self, r: &Regex) -> bool {
        r.is_match(&self.data[..]) || self.header.matches_with(r)
    }

}

impl Display for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.header,
               self.data,
               self.id));
        Ok(())
    }

}

impl Debug for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.id,
               self.header,
               self.data));
        Ok(())
    }

}

#[cfg(test)]
mod test {
    // we use the JSON parser here, so we can generate FileHeaderData
    use storage::json::parser::JsonHeaderParser;
    use storage::file::header::match_header_spec;
    use storage::parser::FileHeaderParser;
    use storage::file::header::data::FileHeaderData as FHD;
    use storage::file::header::spec::FileHeaderSpec as FHS;

    #[test]
    fn test_spec_matching() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser  = JsonHeaderParser::new(Some(spec.clone()));
        let datares = parser.read(Some(text.clone()));
        assert!(datares.is_ok(), "Text could not be parsed: '{}'", text);
        let data = datares.unwrap();

        let matchres = match_header_spec(&spec, &data);
        assert!(matchres.is_none(), "Matching returns error: {:?}", matchres);
    }
}

<commit_msg>Remove unused import<commit_after>use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use regex::Regex;

pub mod id;
pub mod id_type;
pub mod header;
pub mod hash;

use storage::file::id::*;
use self::header::data::*;

/**
 * Internal abstract view on a file. Does not neccessarily exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File {
    /// The name of the module which owns this file
    pub owning_module_name  : &'static str,

    /// The header of the file
    pub header              : FileHeaderData,

    /// The content part of the file
    pub data                : String,

    /// The ID of the file
    pub id                  : FileID,
}

impl File {

    /**
     * Get the owner module name of the file
     */
    pub fn owner_name(&self) -> &'static str {
        self.owning_module_name
    }

    /**
     * Get the header of the file
     */
    pub fn header(&self) -> &FileHeaderData {
        &self.header
    }

    /**
     * Set the header of the file
     */
    pub fn set_header(&mut self, new_header: FileHeaderData) {
        self.header = new_header;
    }

    /**
     * Set the data of the file
     */
    pub fn data(&self) -> &String {
        &self.data
    }

    /**
     * Set the data
     */
    pub fn set_data(&mut self, new_data: String) {
        self.data = new_data;
    }

    /**
     * Set the (header, data) of the file
     */
    pub fn contents(&self) -> (&FileHeaderData, &String) {
        (self.header(), self.data())
    }

    /**
     * Set the id of the file
     */
    pub fn id(&self) -> &FileID {
        &self.id
    }

    /**
     * Check whether the header or the data of the file match some regex
     */
    pub fn matches_with(&self, r: &Regex) -> bool {
        r.is_match(&self.data[..]) || self.header.matches_with(r)
    }

}

impl Display for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.header,
               self.data,
               self.id));
        Ok(())
    }

}

impl Debug for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.id,
               self.header,
               self.data));
        Ok(())
    }

}

#[cfg(test)]
mod test {
    // we use the JSON parser here, so we can generate FileHeaderData
    use storage::json::parser::JsonHeaderParser;
    use storage::file::header::match_header_spec;
    use storage::parser::FileHeaderParser;
    use storage::file::header::spec::FileHeaderSpec as FHS;

    #[test]
    fn test_spec_matching() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser  = JsonHeaderParser::new(Some(spec.clone()));
        let datares = parser.read(Some(text.clone()));
        assert!(datares.is_ok(), "Text could not be parsed: '{}'", text);
        let data = datares.unwrap();

        let matchres = match_header_spec(&spec, &data);
        assert!(matchres.is_none(), "Matching returns error: {:?}", matchres);
    }
}

<|endoftext|>


<commit_before>use std::fmt;

use serde::de::{Error, Unexpected, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use ascii_char::AsciiChar;

impl Serialize for AsciiChar {
    #[inline]
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_char(self.as_char())
    }
}

struct AsciiCharVisitor;

impl<'de> Visitor<'de> for AsciiCharVisitor {
    type Value = AsciiChar;

    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("an ascii character")
    }

    #[inline]
    fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> {
        AsciiChar::from(v).map_err(|_| Error::invalid_value(Unexpected::Char(v), &self))
    }

    #[inline]
    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
        let mut iter = v.chars();
        match (iter.next(), iter.next()) {
            (Some(c), None) => self.visit_char(c),
            _ => Err(Error::invalid_value(Unexpected::Str(v), &self)),
        }
    }
}

impl<'de> Deserialize<'de> for AsciiChar {
    fn deserialize<D>(deserializer: D) -> Result<AsciiChar, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_char(AsciiCharVisitor)
    }
}

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

    #[cfg(feature = "serde_test")]
    const ASCII_CHAR: char = 'e';
    #[cfg(feature = "serde_test")]
    const ASCII_STR: &str = "e";
    #[cfg(feature = "serde_test")]
    const UNICODE_CHAR: char = 'é';

    #[test]
    fn basic() {
        fn assert_serialize<T: Serialize>() {}
        assert_serialize::<AsciiChar>();
        fn assert_deserialize<'de, T: Deserialize<'de>>() {}
        assert_deserialize::<AsciiChar>();
    }

    #[test]
    #[cfg(feature = "serde_test")]
    fn serialize() {
        use serde_test::{assert_tokens, Token};
        let ascii_char = AsciiChar::from(ASCII_CHAR).unwrap();
        assert_tokens(&ascii_char, &[Token::Char(ASCII_CHAR)]);
    }

    #[test]
    #[cfg(feature = "serde_test")]
    fn deserialize() {
        use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
        let ascii_char = AsciiChar::from(ASCII_CHAR).unwrap();
        assert_de_tokens(&ascii_char, &[Token::String(ASCII_STR)]);
        assert_de_tokens(&ascii_char, &[Token::Str(ASCII_STR)]);
        assert_de_tokens(&ascii_char, &[Token::BorrowedStr(ASCII_STR)]);
        assert_de_tokens_error::<AsciiChar>(
            &[Token::Char(UNICODE_CHAR)],
            "invalid value: character `é`, expected an ascii character",
        );
    }
}
<commit_msg>Clearer code but no behavior change for visit_str method<commit_after>use std::fmt;

use serde::de::{Error, Unexpected, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use ascii_char::AsciiChar;

impl Serialize for AsciiChar {
    #[inline]
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_char(self.as_char())
    }
}

struct AsciiCharVisitor;

impl<'de> Visitor<'de> for AsciiCharVisitor {
    type Value = AsciiChar;

    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("an ascii character")
    }

    #[inline]
    fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> {
        AsciiChar::from(v).map_err(|_| Error::invalid_value(Unexpected::Char(v), &self))
    }

    #[inline]
    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
        if v.len() == 1 {
            let c = v.chars().next().unwrap();
            self.visit_char(c)
        } else {
            Err(Error::invalid_value(Unexpected::Str(v), &self))
        }
    }
}

impl<'de> Deserialize<'de> for AsciiChar {
    fn deserialize<D>(deserializer: D) -> Result<AsciiChar, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_char(AsciiCharVisitor)
    }
}

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

    #[cfg(feature = "serde_test")]
    const ASCII_CHAR: char = 'e';
    #[cfg(feature = "serde_test")]
    const ASCII_STR: &str = "e";
    #[cfg(feature = "serde_test")]
    const UNICODE_CHAR: char = 'é';

    #[test]
    fn basic() {
        fn assert_serialize<T: Serialize>() {}
        assert_serialize::<AsciiChar>();
        fn assert_deserialize<'de, T: Deserialize<'de>>() {}
        assert_deserialize::<AsciiChar>();
    }

    #[test]
    #[cfg(feature = "serde_test")]
    fn serialize() {
        use serde_test::{assert_tokens, Token};
        let ascii_char = AsciiChar::from(ASCII_CHAR).unwrap();
        assert_tokens(&ascii_char, &[Token::Char(ASCII_CHAR)]);
    }

    #[test]
    #[cfg(feature = "serde_test")]
    fn deserialize() {
        use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
        let ascii_char = AsciiChar::from(ASCII_CHAR).unwrap();
        assert_de_tokens(&ascii_char, &[Token::String(ASCII_STR)]);
        assert_de_tokens(&ascii_char, &[Token::Str(ASCII_STR)]);
        assert_de_tokens(&ascii_char, &[Token::BorrowedStr(ASCII_STR)]);
        assert_de_tokens_error::<AsciiChar>(
            &[Token::Char(UNICODE_CHAR)],
            "invalid value: character `é`, expected an ascii character",
        );
    }
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use rustc::hir::*;
use utils::{paths, method_chain_args, span_help_and_lint, match_type};

/// **What it does:*** Checks for unnecessary `ok()` in if let.
///
/// **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match on `Ok(x`
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rustc
/// for result in iter {
///     if let Some(bench) = try!(result).parse().ok() {
///         vec.push(bench)
///     }
/// }
/// ```
declare_lint! {
    pub IF_LET_SOME_RESULT,
    Warn,
    "usage of `ok()` in `if let Some(x)` statements is unnecessary, match on `Ok(expr)` instead"
}

#[derive(Copy, Clone)]
pub struct OkIfLetPass;

impl LintPass for OkIfLetPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(IF_LET_SOME_RESULT)
    }
}

impl LateLintPass for OkIfLetPass {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if_let_chain! {[ //begin checking variables
            let ExprMatch(ref op, ref body, ref source) = expr.node, //test if expr is a match
            let MatchSource::IfLetDesugar { .. } = *source, //test if it is an If Let
            let ExprMethodCall(_, _, ref result_types) = op.node, //check is expr.ok() has type Result<T,E>.ok()
            let PatKind::TupleStruct(ref x, ref y, _)  = body[0].pats[0].node, //get operation
            let Some(_) = method_chain_args(op, &["ok"]) //test to see if using ok() methoduse std::marker::Sized;

        ], {
            let is_result_type = match_type(cx, cx.tcx.expr_ty(&result_types[0]), &paths::RESULT);
            let some_expr_string = print::pat_to_string(&y[0]);
            if print::path_to_string(x) == "Some" && is_result_type {
                span_help_and_lint(cx, IF_LET_SOME_RESULT, expr.span,
                "Matching on `Some` with `ok()` is redundant",
                &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string));
            }
        }}
    }
}
<commit_msg>replaced pat_to_string with snippet<commit_after>use rustc::lint::*;
use rustc::hir::*;
use utils::{paths, method_chain_args, span_help_and_lint, match_type, snippet_opt};

/// **What it does:*** Checks for unnecessary `ok()` in if let.
///
/// **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match on `Ok(pat)`
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rustc
/// for result in iter {
///     if let Some(bench) = try!(result).parse().ok() {
///         vec.push(bench)
///     }
/// }
/// ```
declare_lint! {
    pub IF_LET_SOME_RESULT,
    Warn,
    "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
}

#[derive(Copy, Clone)]
pub struct OkIfLetPass;

impl LintPass for OkIfLetPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(IF_LET_SOME_RESULT)
    }
}

impl LateLintPass for OkIfLetPass {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if_let_chain! {[ //begin checking variables
            let ExprMatch(ref op, ref body, ref source) = expr.node, //test if expr is a match
            let MatchSource::IfLetDesugar { .. } = *source, //test if it is an If Let
            let ExprMethodCall(_, _, ref result_types) = op.node, //check is expr.ok() has type Result<T,E>.ok()
            let PatKind::TupleStruct(ref x, ref y, _)  = body[0].pats[0].node, //get operation
            let Some(some_expr_string) = snippet_opt(cx, y[0].span),
            let Some(_) = method_chain_args(op, &["ok"]) //test to see if using ok() methoduse std::marker::Sized;

        ], {
            let is_result_type = match_type(cx, cx.tcx.expr_ty(&result_types[0]), &paths::RESULT);
            if print::path_to_string(x) == "Some" && is_result_type {
                span_help_and_lint(cx, IF_LET_SOME_RESULT, expr.span,
                "Matching on `Some` with `ok()` is redundant",
                &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string));
            }
        }}
    }
}
<|endoftext|>


<commit_before>//! Ranges.
//!
//! This module contains the main primitive in zmicro.

/// A range.
///
/// This is the main primitive in the range encoding techniques. Ranges represent some variable
/// size block of the stream, which breaks down to bits.
///
/// Bits are read and written according to some probability. When a bit is written, the range
/// updates to a smaller subrange. The subrange's size is determined according to the probability
/// of the bit being 0. If the bit written is 0, and the probability pridiction was `Pr(0) = p`,
/// then the range's new size is `p·r` with `r` being the old size.
///
/// Ranges has an important invariant: They can never be of zero size. If a range is of size 1,
/// however, the range is said to be _exhausted_, meaning that it cannot store any more
/// information.
///
/// This concept might seem weird as first, but it is incredibly logical: The more correct the
/// prediction is, the less the range narrows, and consequently it is exhausted earlier, meaning
/// that it can store more information.
///
/// As such, the efficiency depends entirely on the predictor, and the only theoretical bound that
/// exists is the state space.
struct Range {
    /// The start of the range.
    start: u32,
    /// The length of the range.
    len: u32,
}

impl Range {
    /// Create a full size range.
    fn full() -> Range {
        Range {
            start: 0,
            len: !0,
        }
    }

    /// Write a bit into the range.
    ///
    /// This pushes `bit` to the stream represented in the range, with the probability `pr_0` of
    /// being 0 (`false`).
    ///
    /// `pr_0` linearly corresponds to the probability, but is represented as an integer for
    /// performance reasons. As such, 0.5 corresponds to 0x7FFFFFFF, 1 corresponds to 0xFFFFFFFF,
    /// and 0 corresponds to 0.
    ///
    /// The returned boolean is true if there is space for more bits.
    fn write(&mut self, bit: bool, pr_0: u32) -> bool {
        // Fetch the length for performance reasons.
        let len = self.len;

        debug_assert!(len > 1, "The current length of the range is too small to contain more \
                      bits, please renormalize/flush.");

        // Calculate the new length of the left subrange (i.e. the length of the range if the bit
        // is 0):
        //
        // - Cast the integers to 64-bit to avoid any overflows.
        // - Multiply the current length by the integer representing a probability. The integer
        //   corresponds linearly to the probability on the unit interval.
        // - Divide by the maximal value of the unnormalized probability, in order to normalize the
        //   result:
        //     - Add half the maximal value to shift the result and flip the results which would be
        //       have decimal parts above .5 to the next number.
        //     - Shift 32 bits downwards (equivalent to floored division by the maximal
        //       unnormalized probability).
        // - Cast back to 32-bit integer truncating only 0s because of the above bit shift.
        let mut len_0 = ((len as u64 * pr_0 as u64 + 0x80000000) >> 32) as u32;

        // Normalize len_0 to avoid zero intervals.
        if len_0 == 0 {
            // len_0 rounded down to an empty range. This means that the left subrange for bit 0 is
            // empty, and thus not representable. For this reason, we round up to 1 just to make
            // the subrange representable.
            len_0 = 1;
        } else if len_0 == len {
            // len_0 rounded up to the length of the current range. This means that the right
            // subrange for bit 1 will be empty, and thus not representable. For this reason, we
            // leap to a range which is one unit shorter than the current one, leaving a right
            // subrange of length 1.
            len_0 = len - 1;
        }

        debug_assert!(len_0 < len, "The length of the left subrange must be smaller than the \
                      current range's length.");

        if bit {
            // The bit was 1.

            // Refine the range to the second half:
            //
            //     [            self.len           ]
            //     [  len_0  ][  self.len - len_0  ]
            //                \~~~~~~~~~~~~~~~~~~~~/
            //                 This is the new range
            self.len   -= len_0;
            self.start += len_0;
        } else {
            // The bit was 0.

            // The start of the range is fixed, but update the length of the range.
            self.len = len_0;
        }

        // It's not exhausted if the length is stil above one.
        self.len > 1
    }

    /// Read a bit from the range.
    ///
    /// This reads the top bit (first written bit) with probability `pr_0` from the range, and
    /// updates the range such that the second bit is the new first (similar to `pop` but FIFO).
    ///
    /// `pr_0` must not be 0xFFFFFFFF or 0.
    ///
    /// The probability `pr_0` **must** match the probability given when the bit was written into
    /// the range.
    ///
    /// `None` is returned if the range is exhausted and no more bits are stored in the range.
    fn read(&mut self, pr_0: u32) -> Option<bool> {
        debug_assert!(self.len != 0, "Zero-sized ranges are invalid.");
        debug_assert!(pr_0 != 0, "Pr(0) can't be 0.");
        debug_assert!(pr_0 != !0, "Pr(0) can't be 1.");

        if self.len == !0 {
            // The range cannot contain more information, so no more bits can be extracted from
            // this range.
            None
        } else {
            // Determine if the start false on the left or right half (simply compare to the
            // probability).
            if self.start < pr_0 {
                // The bit is 0.

                // Rescale the length to get the left superrange:
                //
                //     [ rescaled range ]
                //     [ left ][  right ]
                //
                // The left range is of size `pr_0`. Now, we divide by this number, but we double
                // the width to get the desired precision.
                self.len = (((self.len as u64) << 32) / pr_0 as u64) as u32;

                // The start of the range is fixed, because the read bit is zer.

                Some(false)
            } else {
                // The bit is 1.

                // Negate the probability to get Pr(1).
                let pr_1 = !0 - pr_0;
                // We repeat the same as above conditional, except that we use `pr_1` instead of
                // `pr_0`.
                self.len = (((self.len as u64) << 32) / pr_1 as u64) as u32;

                // Subtract the length of the left half to "shift" the range towards the left in
                // order to complete the transformation. This means that the new offset is the
                // start of the right range.
                self.start -= pr_0;

                Some(true)
            }
        }
    }
}

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

    #[test]
    fn write_read() {
        let mut range = Range::full();

        range.write(true, 5000000);
        range.write(true, 2999);
        range.write(false, 5000000000);
        range.write(false, 50000000);
        range.write(true, 333333332999);

        assert!( range.read(5000000).unwrap());
        assert!( range.read(2999).unwrap());
        assert!(!range.read(5000000000).unwrap());
        assert!(!range.read(50000000).unwrap());
        assert!( range.read(333333332999).unwrap());
    }

    #[test]
    fn write_ones() {
        let mut range = Range::full();

        let mut n = 0;
        while range.write(true, 500) {
            n += 1;
        }

        for i in 0..n {
            assert_eq!(range.read(500), Some(true));
        }

        assert_eq!(range.read(500), None);
    }

    #[test]
    fn balanced_ones() {
        let mut range = Range::full();

        while range.write(true, 0x80000000) {
            println!("start = {:x}", range.start);
            println!("len   = {:x}", range.len);
        }

            println!("start = {:x}", range.start);
            println!("len   = {:x}", range.len);

        assert_eq!(range.start, 0xFFFFFFFF);
    }

    #[test]
    fn balanced_zeros() {
        let mut range = Range::full();

        while range.write(false, 0x80000000) {}

        assert_eq!(range.start, 0);
    }
}
<commit_msg>Fix yet another off-by-one bug.<commit_after>//! Ranges.
//!
//! This module contains the main primitive in zmicro.

/// A range.
///
/// This is the main primitive in the range encoding techniques. Ranges represent some variable
/// size block of the stream, which breaks down to bits.
///
/// Bits are read and written according to some probability. When a bit is written, the range
/// updates to a smaller subrange. The subrange's size is determined according to the probability
/// of the bit being 0. If the bit written is 0, and the probability pridiction was `Pr(0) = p`,
/// then the range's new size is `p·r` with `r` being the old size.
///
/// Ranges has an important invariant: They can never be of zero size. If a range is of size 1,
/// however, the range is said to be _exhausted_, meaning that it cannot store any more
/// information.
///
/// This concept might seem weird as first, but it is incredibly logical: The more correct the
/// prediction is, the less the range narrows, and consequently it is exhausted earlier, meaning
/// that it can store more information.
///
/// As such, the efficiency depends entirely on the predictor, and the only theoretical bound that
/// exists is the state space.
struct Range {
    /// The start of the range.
    start: u32,
    /// The length of the range, minus one.
    ///
    /// The reason it is one below the logical size is that we need to be able to represent the
    /// full range.
    len_minus_one: u32,
}

impl Range {
    /// Create a full size range.
    fn full() -> Range {
        Range {
            start: 0,
            len_minus_one: !0,
        }
    }

    /// Write a bit into the range.
    ///
    /// This pushes `bit` to the stream represented in the range, with the probability `pr_0` of
    /// being 0 (`false`).
    ///
    /// `pr_0` linearly corresponds to the probability, but is represented as an integer for
    /// performance reasons. As such, 0.5 corresponds to 0x7FFFFFFF, 1 corresponds to 0xFFFFFFFF,
    /// and 0 corresponds to 0.
    ///
    /// The returned boolean is true if there is space for more bits.
    fn write(&mut self, bit: bool, pr_0: u32) -> bool {
        // Fetch the length for performance reasons.
        let len_minus_one = self.len_minus_one;

        // Calculate the new length of the left subrange (i.e. the length of the range if the bit
        // is 0):
        //
        // - Cast the integers to 64-bit to avoid any overflows.
        // - Multiply the current length by the integer representing a probability. The integer
        //   corresponds linearly to the probability on the unit interval.
        // - Divide by the maximal value of the unnormalized probability, in order to normalize the
        //   result:
        //     - Add half the maximal value to shift the result and flip the results which would be
        //       have decimal parts above .5 to the next number.
        //     - Shift 32 bits downwards (equivalent to floored division by the maximal
        //       unnormalized probability).
        // - Cast back to 32-bit integer truncating only 0s because of the above bit shift.
        let mut len_0 = (((len_minus_one as u64 + 1) * pr_0 as u64 + 0x80000000) >> 32) as u32;

        // Normalize len_0 to avoid zero intervals.
        if len_0 == 0 {
            // len_0 rounded down to an empty range. This means that the left subrange for bit 0 is
            // empty, and thus not representable. For this reason, we round up to 1 just to make
            // the subrange representable.
            len_0 = 1;
        } else if len_0 - 1 == len_minus_one {
            // len_0 rounded up to the length of the current range. This means that the right
            // subrange for bit 1 will be empty, and thus not representable. For this reason, we
            // leap to a range which is one unit shorter than the current one, leaving a right
            // subrange of length 1.
            len_0 = len_minus_one;
        }

        if bit {
            // The bit was 1.

            // Refine the range to the second half:
            //
            //     [            len           ]
            //     [  len_0  ][  len - len_0  ]
            //                \~~~~~~~~~~~~~~~/
            //              This is the new range
            self.len_minus_one -= len_0;
            self.start += len_0;
        } else {
            // The bit was 0.

            // The start of the range is fixed, but update the length of the range.
            self.len_minus_one = len_0 - 1;
        }

        // It's not exhausted if the length is stil above one.
        self.len_minus_one != 0
    }

    /// Read a bit from the range.
    ///
    /// This reads the top bit (first written bit) with probability `pr_0` from the range, and
    /// updates the range such that the second bit is the new first (similar to `pop` but FIFO).
    ///
    /// `pr_0` must not be 0xFFFFFFFF or 0.
    ///
    /// The probability `pr_0` **must** match the probability given when the bit was written into
    /// the range.
    ///
    /// `None` is returned if the range is exhausted and no more bits are stored in the range.
    fn read(&mut self, pr_0: u32) -> Option<bool> {
        debug_assert!(pr_0 != 0, "Pr(0) can't be 0.");
        debug_assert!(pr_0 != !0, "Pr(0) can't be 1.");

        if self.len_minus_one == !0 {
            // The range cannot contain more information, so no more bits can be extracted from
            // this range.
            None
        } else {
            // Determine if the start false on the left or right half (simply compare to the
            // probability).
            if self.start < pr_0 {
                // The bit is 0.

                // Rescale the length to get the left superrange:
                //
                //     [ rescaled range ]
                //     [ left ][  right ]
                //
                // The left range is of size `pr_0`. Now, we divide by this number, but we double
                // the width to get the desired precision.
                self.len_minus_one = (((self.len_minus_one as u64 + 1) << 32) / pr_0 as u64) as u32 - 1;

                // The start of the range is fixed, because the read bit is zer.

                Some(false)
            } else {
                // The bit is 1.

                // Negate the probability to get Pr(1).
                let pr_1 = !0 - pr_0;
                // We repeat the same as above conditional, except that we use `pr_1` instead of
                // `pr_0`.
                self.len_minus_one = (((self.len_minus_one as u64 + 1) << 32) / pr_1 as u64) as u32 - 1;

                // Subtract the length of the left half to "shift" the range towards the left in
                // order to complete the transformation. This means that the new offset is the
                // start of the right range.
                self.start -= pr_0;

                Some(true)
            }
        }
    }
}

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

    #[test]
    fn write_read() {
        let mut range = Range::full();

        range.write(true, 5000000);
        range.write(true, 2999);
        range.write(false, 5000000000);
        range.write(false, 50000000);
        range.write(true, 333333332999);

        assert!( range.read(5000000).unwrap());
        assert!( range.read(2999).unwrap());
        assert!(!range.read(5000000000).unwrap());
        assert!(!range.read(50000000).unwrap());
        assert!( range.read(333333332999).unwrap());
    }

    #[test]
    fn write_ones() {
        let mut range = Range::full();

        let mut n = 0;
        while range.write(true, 500) {
            n += 1;
        }

        for i in 0..n {
            assert_eq!(range.read(500), Some(true));
        }

        assert_eq!(range.read(500), None);
    }

    #[test]
    fn balanced_ones() {
        let mut range = Range::full();

        while range.write(true, 0x80000000) {}

        assert_eq!(range.start, 0xFFFFFFFF);
    }

    #[test]
    fn balanced_zeros() {
        let mut range = Range::full();

        while range.write(false, 0x80000000) {}

        assert_eq!(range.start, 0);
    }

    #[test]
    fn unbalanced_ones() {
        let mut range = Range::full();

        while range.write(true, 30482) {}

        assert_eq!(range.start, 0xFFFFFFFF);
    }
}
<|endoftext|>