<commit_before>pub mod gcra;
pub mod leaky_bucket;

pub use self::gcra::*;
pub use self::leaky_bucket::*;

use {instant, InconsistentCapacity, NegativeMultiDecision};

use lib::*;

/// The default rate limiting algorithm in this crate: The ["leaky
/// bucket"](leaky_bucket/struct.LeakyBucket.html).
///
/// The leaky bucket algorithm is fairly easy to understand and has
/// decent performance in most cases. If better threaded performance
/// is needed, this crate also offers the
/// [`GCRA`](gcra/struct.GCRA.html) algorithm.
pub type DefaultAlgorithm = LeakyBucket;

/// Provides additional information about non-conforming cells, most
/// importantly the earliest time until the next cell could be
/// considered conforming.
///
/// Since this does not account for effects like thundering herds,
/// users should always add random jitter to the times given.
pub trait NonConformance<P: instant::Relative> {
    /// Returns the earliest time at which a decision could be
    /// conforming (excluding conforming decisions made by the Decider
    /// that are made in the meantime).
    fn earliest_possible(&self) -> P;

    /// Returns the minimum amount of time from the time that the
    /// decision was made (relative to the `at` argument in a
    /// `Decider`'s `check_at` method) that must pass before a
    /// decision can be conforming. Since Durations can not be
    /// negative, a zero duration is returned if `from` is already
    /// after that duration.
    fn wait_time_from(&self, from: P) -> Duration {
        let earliest = self.earliest_possible();
        earliest.duration_since(earliest.min(from))
    }
}

pub trait NonConformanceExt<P: instant::Absolute>: NonConformance<P> {
    /// Returns the minimum amount of time (down to 0) that needs to
    /// pass from the current instant for the Decider to consider a
    /// cell conforming again.
    fn wait_time(&self) -> Duration {
        self.wait_time_from(P::now())
    }
}

impl<P: instant::Absolute, T> NonConformanceExt<P> for T where T: NonConformance<P> {}

/// The trait that implementations of metered rate-limiter algorithms
/// have to implement.
///
/// Implementing structures are expected to represent the "parameters"
/// (e.g., the allowed requests/s), and keep the information necessary
/// to make a decision, e.g. concrete usage statistics for an
/// in-memory rate limiter, in the associated structure
/// [`BucketState`](#associatedtype.BucketState).
pub trait Algorithm<P: instant::Relative = instant::TimeSource>:
    Send + Sync + Sized + fmt::Debug
{
    /// The state of a single rate limiting bucket.
    ///
    /// Every new rate limiting state is initialized as `Default`. The
    /// states must be safe to share across threads (this crate uses a
    /// `parking_lot` Mutex to allow that).
    type BucketState: RateLimitState<Self, P>;

    /// The type returned when a rate limiting decision for a single
    /// cell is negative. Each rate limiting algorithm can decide to
    /// return the type that suits it best, but most algorithms'
    /// decisions also implement
    /// [`NonConformance`](trait.NonConformance.html), to ease
    /// handling of how long to wait.
    type NegativeDecision: PartialEq + fmt::Display + fmt::Debug + Send + Sync;

    /// Constructs a rate limiter with the given parameters:
    /// `capacity` is the number of cells to allow, weighing
    /// `cell_weight`, every `per_time_unit`.
    fn construct(
        capacity: NonZeroU32,
        cell_weight: NonZeroU32,
        per_time_unit: Duration,
    ) -> Result<Self, InconsistentCapacity>;

    /// Tests if `n` cells can be accommodated in the rate limiter at
    /// the instant `at` and updates the rate-limiter state to account
    /// for the weight of the cells and updates the ratelimiter state.
    ///
    /// The update is all or nothing: Unless all n cells can be
    /// accommodated, the state of the rate limiter will not be
    /// updated.
    fn test_n_and_update(
        &self,
        state: &Self::BucketState,
        n: u32,
        at: P,
    ) -> Result<(), NegativeMultiDecision<Self::NegativeDecision>>;

    /// Tests if a single cell can be accommodated in the rate limiter
    /// at the instant `at` and updates the rate-limiter state to
    /// account for the weight of the cell.
    ///
    /// This method is provided by default, using the `n` test&update
    /// method.
    fn test_and_update(
        &self,
        state: &Self::BucketState,
        at: P,
    ) -> Result<(), Self::NegativeDecision> {
        match self.test_n_and_update(state, 1, at) {
            Ok(()) => Ok(()),
            Err(NegativeMultiDecision::BatchNonConforming(1, nc)) => Err(nc),
            Err(other) => unreachable!(
                "BUG: measuring a batch of size 1 reported insufficient capacity: {:?}",
                other
            ),
        }
    }
}

/// Trait that all rate limit states have to implement around
/// housekeeping in keyed rate limiters.
pub trait RateLimitState<P, I: instant::Relative>: Default + Send + Sync + Eq + fmt::Debug {}

/// Trait that all rate limit states implement if there is a real-time
/// clock available.
pub trait RateLimitStateWithClock<P, I: instant::Absolute>: RateLimitState<P, I> {
    /// Returns the last time instant that the state had any relevance
    /// (i.e. the rate limiter would behave exactly as if it was a new
    /// rate limiter after this time).
    ///
    /// If the state has not been touched for a given amount of time,
    /// the keyed rate limiter will expire it.
    ///
    /// # Thread safety
    /// This uses a bucket state snapshot to determine eligibility;
    /// race conditions can occur.
    fn last_touched(&self, params: &P) -> I;
}

#[cfg(feature = "std")]
mod std {
    use evmap::ShallowCopy;
    use instant;

    /// Trait implemented by all rate limit states that are compatible
    /// with the KeyedRateLimiters.
    pub trait KeyableRateLimitState<P, I: instant::Absolute>:
        super::RateLimitStateWithClock<P, I> + ShallowCopy
    {
    }

    #[cfg(feature = "std")]
    impl<T, P, I> KeyableRateLimitState<P, I> for T
    where
        T: super::RateLimitStateWithClock<P, I> + ShallowCopy,
        I: instant::Absolute,
    {
    }
}

#[cfg(feature = "std")]
pub use self::std::*;
<commit_msg>Default the type argument on NonConformance<commit_after>pub mod gcra;
pub mod leaky_bucket;

pub use self::gcra::*;
pub use self::leaky_bucket::*;

use {instant, InconsistentCapacity, NegativeMultiDecision};

use lib::*;

/// The default rate limiting algorithm in this crate: The ["leaky
/// bucket"](leaky_bucket/struct.LeakyBucket.html).
///
/// The leaky bucket algorithm is fairly easy to understand and has
/// decent performance in most cases. If better threaded performance
/// is needed, this crate also offers the
/// [`GCRA`](gcra/struct.GCRA.html) algorithm.
pub type DefaultAlgorithm = LeakyBucket;

/// Provides additional information about non-conforming cells, most
/// importantly the earliest time until the next cell could be
/// considered conforming.
///
/// Since this does not account for effects like thundering herds,
/// users should always add random jitter to the times given.
pub trait NonConformance<P: instant::Relative = instant::TimeSource> {
    /// Returns the earliest time at which a decision could be
    /// conforming (excluding conforming decisions made by the Decider
    /// that are made in the meantime).
    fn earliest_possible(&self) -> P;

    /// Returns the minimum amount of time from the time that the
    /// decision was made (relative to the `at` argument in a
    /// `Decider`'s `check_at` method) that must pass before a
    /// decision can be conforming. Since Durations can not be
    /// negative, a zero duration is returned if `from` is already
    /// after that duration.
    fn wait_time_from(&self, from: P) -> Duration {
        let earliest = self.earliest_possible();
        earliest.duration_since(earliest.min(from))
    }
}

pub trait NonConformanceExt<P: instant::Absolute>: NonConformance<P> {
    /// Returns the minimum amount of time (down to 0) that needs to
    /// pass from the current instant for the Decider to consider a
    /// cell conforming again.
    fn wait_time(&self) -> Duration {
        self.wait_time_from(P::now())
    }
}

impl<P: instant::Absolute, T> NonConformanceExt<P> for T where T: NonConformance<P> {}

/// The trait that implementations of metered rate-limiter algorithms
/// have to implement.
///
/// Implementing structures are expected to represent the "parameters"
/// (e.g., the allowed requests/s), and keep the information necessary
/// to make a decision, e.g. concrete usage statistics for an
/// in-memory rate limiter, in the associated structure
/// [`BucketState`](#associatedtype.BucketState).
pub trait Algorithm<P: instant::Relative = instant::TimeSource>:
    Send + Sync + Sized + fmt::Debug
{
    /// The state of a single rate limiting bucket.
    ///
    /// Every new rate limiting state is initialized as `Default`. The
    /// states must be safe to share across threads (this crate uses a
    /// `parking_lot` Mutex to allow that).
    type BucketState: RateLimitState<Self, P>;

    /// The type returned when a rate limiting decision for a single
    /// cell is negative. Each rate limiting algorithm can decide to
    /// return the type that suits it best, but most algorithms'
    /// decisions also implement
    /// [`NonConformance`](trait.NonConformance.html), to ease
    /// handling of how long to wait.
    type NegativeDecision: PartialEq + fmt::Display + fmt::Debug + Send + Sync;

    /// Constructs a rate limiter with the given parameters:
    /// `capacity` is the number of cells to allow, weighing
    /// `cell_weight`, every `per_time_unit`.
    fn construct(
        capacity: NonZeroU32,
        cell_weight: NonZeroU32,
        per_time_unit: Duration,
    ) -> Result<Self, InconsistentCapacity>;

    /// Tests if `n` cells can be accommodated in the rate limiter at
    /// the instant `at` and updates the rate-limiter state to account
    /// for the weight of the cells and updates the ratelimiter state.
    ///
    /// The update is all or nothing: Unless all n cells can be
    /// accommodated, the state of the rate limiter will not be
    /// updated.
    fn test_n_and_update(
        &self,
        state: &Self::BucketState,
        n: u32,
        at: P,
    ) -> Result<(), NegativeMultiDecision<Self::NegativeDecision>>;

    /// Tests if a single cell can be accommodated in the rate limiter
    /// at the instant `at` and updates the rate-limiter state to
    /// account for the weight of the cell.
    ///
    /// This method is provided by default, using the `n` test&update
    /// method.
    fn test_and_update(
        &self,
        state: &Self::BucketState,
        at: P,
    ) -> Result<(), Self::NegativeDecision> {
        match self.test_n_and_update(state, 1, at) {
            Ok(()) => Ok(()),
            Err(NegativeMultiDecision::BatchNonConforming(1, nc)) => Err(nc),
            Err(other) => unreachable!(
                "BUG: measuring a batch of size 1 reported insufficient capacity: {:?}",
                other
            ),
        }
    }
}

/// Trait that all rate limit states have to implement around
/// housekeeping in keyed rate limiters.
pub trait RateLimitState<P, I: instant::Relative>: Default + Send + Sync + Eq + fmt::Debug {}

/// Trait that all rate limit states implement if there is a real-time
/// clock available.
pub trait RateLimitStateWithClock<P, I: instant::Absolute>: RateLimitState<P, I> {
    /// Returns the last time instant that the state had any relevance
    /// (i.e. the rate limiter would behave exactly as if it was a new
    /// rate limiter after this time).
    ///
    /// If the state has not been touched for a given amount of time,
    /// the keyed rate limiter will expire it.
    ///
    /// # Thread safety
    /// This uses a bucket state snapshot to determine eligibility;
    /// race conditions can occur.
    fn last_touched(&self, params: &P) -> I;
}

#[cfg(feature = "std")]
mod std {
    use evmap::ShallowCopy;
    use instant;

    /// Trait implemented by all rate limit states that are compatible
    /// with the KeyedRateLimiters.
    pub trait KeyableRateLimitState<P, I: instant::Absolute>:
        super::RateLimitStateWithClock<P, I> + ShallowCopy
    {
    }

    #[cfg(feature = "std")]
    impl<T, P, I> KeyableRateLimitState<P, I> for T
    where
        T: super::RateLimitStateWithClock<P, I> + ShallowCopy,
        I: instant::Absolute,
    {
    }
}

#[cfg(feature = "std")]
pub use self::std::*;
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};
use std::ops::Range;
use ucd_parse::Codepoints;

mod case_mapping;
mod raw_emitter;
mod unicode_download;

use raw_emitter::{emit_codepoints, RawEmitter};

static PROPERTIES: &[&str] = &[
    "Alphabetic",
    "Lowercase",
    "Uppercase",
    "Cased",
    "Case_Ignorable",
    "Grapheme_Extend",
    "White_Space",
    "Cc",
    "N",
];

struct UnicodeData {
    ranges: Vec<(&'static str, Vec<Range<u32>>)>,
    to_upper: BTreeMap<u32, (u32, u32, u32)>,
    to_lower: BTreeMap<u32, (u32, u32, u32)>,
}

fn to_mapping(origin: u32, codepoints: Vec<ucd_parse::Codepoint>) -> Option<(u32, u32, u32)> {
    let mut a = None;
    let mut b = None;
    let mut c = None;

    for codepoint in codepoints {
        if origin == codepoint.value() {
            return None;
        }

        if a.is_none() {
            a = Some(codepoint.value());
        } else if b.is_none() {
            b = Some(codepoint.value());
        } else if c.is_none() {
            c = Some(codepoint.value());
        } else {
            panic!("more than 3 mapped codepoints")
        }
    }

    Some((a.unwrap(), b.unwrap_or(0), c.unwrap_or(0)))
}

static UNICODE_DIRECTORY: &str = "unicode-downloads";

fn load_data() -> UnicodeData {
    unicode_download::fetch_latest();

    let mut properties = HashMap::new();
    for row in ucd_parse::parse::<_, ucd_parse::CoreProperty>(&UNICODE_DIRECTORY).unwrap() {
        if let Some(name) = PROPERTIES.iter().find(|prop| **prop == row.property.as_str()) {
            properties.entry(*name).or_insert_with(Vec::new).push(row.codepoints);
        }
    }
    for row in ucd_parse::parse::<_, ucd_parse::Property>(&UNICODE_DIRECTORY).unwrap() {
        if let Some(name) = PROPERTIES.iter().find(|prop| **prop == row.property.as_str()) {
            properties.entry(*name).or_insert_with(Vec::new).push(row.codepoints);
        }
    }

    let mut to_lower = BTreeMap::new();
    let mut to_upper = BTreeMap::new();
    for row in ucd_parse::UnicodeDataExpander::new(
        ucd_parse::parse::<_, ucd_parse::UnicodeData>(&UNICODE_DIRECTORY).unwrap(),
    ) {
        let general_category = if ["Nd", "Nl", "No"].contains(&row.general_category.as_str()) {
            "N"
        } else {
            row.general_category.as_str()
        };
        if let Some(name) = PROPERTIES.iter().find(|prop| **prop == general_category) {
            properties
                .entry(*name)
                .or_insert_with(Vec::new)
                .push(Codepoints::Single(row.codepoint));
        }

        if let Some(mapped) = row.simple_lowercase_mapping {
            if mapped != row.codepoint {
                to_lower.insert(row.codepoint.value(), (mapped.value(), 0, 0));
            }
        }
        if let Some(mapped) = row.simple_uppercase_mapping {
            if mapped != row.codepoint {
                to_upper.insert(row.codepoint.value(), (mapped.value(), 0, 0));
            }
        }
    }

    for row in ucd_parse::parse::<_, ucd_parse::SpecialCaseMapping>(&UNICODE_DIRECTORY).unwrap() {
        if !row.conditions.is_empty() {
            // Skip conditional case mappings
            continue;
        }

        let key = row.codepoint.value();
        if let Some(lower) = to_mapping(key, row.lowercase) {
            to_lower.insert(key, lower);
        }
        if let Some(upper) = to_mapping(key, row.uppercase) {
            to_upper.insert(key, upper);
        }
    }

    let mut properties: HashMap<&'static str, Vec<Range<u32>>> = properties
        .into_iter()
        .map(|(k, v)| {
            (
                k,
                v.into_iter()
                    .flat_map(|codepoints| match codepoints {
                        Codepoints::Single(c) => c
                            .scalar()
                            .map(|ch| (ch as u32..ch as u32 + 1))
                            .into_iter()
                            .collect::<Vec<_>>(),
                        Codepoints::Range(c) => c
                            .into_iter()
                            .flat_map(|c| c.scalar().map(|ch| (ch as u32..ch as u32 + 1)))
                            .collect::<Vec<_>>(),
                    })
                    .collect::<Vec<Range<u32>>>(),
            )
        })
        .collect();

    for ranges in properties.values_mut() {
        merge_ranges(ranges);
    }

    let mut properties = properties.into_iter().collect::<Vec<_>>();
    properties.sort_by_key(|p| p.0);
    UnicodeData { ranges: properties, to_lower, to_upper }
}

fn main() {
    let write_location = std::env::args().nth(1).unwrap_or_else(|| {
        eprintln!("Must provide path to write unicode tables to");
        eprintln!(
            "e.g. {} src/libcore/unicode/unicode_data.rs",
            std::env::args().next().unwrap_or_default()
        );
        std::process::exit(1);
    });

    let unicode_data = load_data();
    let ranges_by_property = &unicode_data.ranges;

    let mut total_bytes = 0;
    let mut modules = Vec::new();
    for (property, ranges) in ranges_by_property {
        let datapoints = ranges.iter().map(|r| r.end - r.start).sum::<u32>();
        let mut emitter = RawEmitter::new();
        emit_codepoints(&mut emitter, &ranges);

        modules.push((property.to_lowercase().to_string(), emitter.file));
        println!("{:15}: {} bytes, {} codepoints", property, emitter.bytes_used, datapoints,);
        total_bytes += emitter.bytes_used;
    }

    let mut table_file = String::new();

    table_file.push_str(
        "///! This file is generated by src/tools/unicode-table-generator; do not edit manually!\n",
    );

    table_file.push_str("use super::range_search;\n\n");

    table_file.push_str(&version());

    table_file.push('\n');

    modules.push((String::from("conversions"), case_mapping::generate_case_mapping(&unicode_data)));

    for (name, contents) in modules {
        table_file.push_str("#[rustfmt::skip]\n");
        table_file.push_str(&format!("pub mod {} {{\n", name));
        for line in contents.lines() {
            if !line.trim().is_empty() {
                table_file.push_str("    ");
                table_file.push_str(&line);
            }
            table_file.push('\n');
        }
        table_file.push_str("}\n\n");
    }

    std::fs::write(&write_location, format!("{}\n", table_file.trim_end())).unwrap();

    println!("Total table sizes: {} bytes", total_bytes);
}

fn version() -> String {
    let mut out = String::new();
    out.push_str("pub const UNICODE_VERSION: (u32, u32, u32) = ");

    let readme =
        std::fs::read_to_string(std::path::Path::new(UNICODE_DIRECTORY).join("ReadMe.txt"))
            .unwrap();

    let prefix = "for Version ";
    let start = readme.find(prefix).unwrap() + prefix.len();
    let end = readme.find(" of the Unicode Standard.").unwrap();
    let version =
        readme[start..end].split('.').map(|v| v.parse::<u32>().expect(&v)).collect::<Vec<_>>();
    let [major, minor, micro] = [version[0], version[1], version[2]];

    out.push_str(&format!("({}, {}, {});\n", major, minor, micro));
    out
}

fn fmt_list<V: std::fmt::Debug>(values: impl IntoIterator<Item = V>) -> String {
    let pieces = values.into_iter().map(|b| format!("{:?}, ", b)).collect::<Vec<_>>();
    let mut out = String::new();
    let mut line = format!("\n    ");
    for piece in pieces {
        if line.len() + piece.len() < 98 {
            line.push_str(&piece);
        } else {
            out.push_str(line.trim_end());
            out.push('\n');
            line = format!("    {}", piece);
        }
    }
    out.push_str(line.trim_end());
    out.push('\n');
    out
}

fn merge_ranges(ranges: &mut Vec<Range<u32>>) {
    loop {
        let mut new_ranges = Vec::new();
        let mut idx_iter = 0..(ranges.len() - 1);
        while let Some(idx) = idx_iter.next() {
            let cur = ranges[idx].clone();
            let next = ranges[idx + 1].clone();
            if cur.end == next.start {
                let _ = idx_iter.next(); // skip next as we're merging it in
                new_ranges.push(cur.start..next.end);
            } else {
                new_ranges.push(cur);
            }
        }
        new_ranges.push(ranges.last().unwrap().clone());
        if new_ranges.len() == ranges.len() {
            *ranges = new_ranges;
            break;
        } else {
            *ranges = new_ranges;
        }
    }
}
<commit_msg>Generate tests for Unicode property data<commit_after>use std::collections::{BTreeMap, HashMap};
use std::ops::Range;
use ucd_parse::Codepoints;

mod case_mapping;
mod raw_emitter;
mod unicode_download;

use raw_emitter::{emit_codepoints, RawEmitter};

static PROPERTIES: &[&str] = &[
    "Alphabetic",
    "Lowercase",
    "Uppercase",
    "Cased",
    "Case_Ignorable",
    "Grapheme_Extend",
    "White_Space",
    "Cc",
    "N",
];

struct UnicodeData {
    ranges: Vec<(&'static str, Vec<Range<u32>>)>,
    to_upper: BTreeMap<u32, (u32, u32, u32)>,
    to_lower: BTreeMap<u32, (u32, u32, u32)>,
}

fn to_mapping(origin: u32, codepoints: Vec<ucd_parse::Codepoint>) -> Option<(u32, u32, u32)> {
    let mut a = None;
    let mut b = None;
    let mut c = None;

    for codepoint in codepoints {
        if origin == codepoint.value() {
            return None;
        }

        if a.is_none() {
            a = Some(codepoint.value());
        } else if b.is_none() {
            b = Some(codepoint.value());
        } else if c.is_none() {
            c = Some(codepoint.value());
        } else {
            panic!("more than 3 mapped codepoints")
        }
    }

    Some((a.unwrap(), b.unwrap_or(0), c.unwrap_or(0)))
}

static UNICODE_DIRECTORY: &str = "unicode-downloads";

fn load_data() -> UnicodeData {
    unicode_download::fetch_latest();

    let mut properties = HashMap::new();
    for row in ucd_parse::parse::<_, ucd_parse::CoreProperty>(&UNICODE_DIRECTORY).unwrap() {
        if let Some(name) = PROPERTIES.iter().find(|prop| **prop == row.property.as_str()) {
            properties.entry(*name).or_insert_with(Vec::new).push(row.codepoints);
        }
    }
    for row in ucd_parse::parse::<_, ucd_parse::Property>(&UNICODE_DIRECTORY).unwrap() {
        if let Some(name) = PROPERTIES.iter().find(|prop| **prop == row.property.as_str()) {
            properties.entry(*name).or_insert_with(Vec::new).push(row.codepoints);
        }
    }

    let mut to_lower = BTreeMap::new();
    let mut to_upper = BTreeMap::new();
    for row in ucd_parse::UnicodeDataExpander::new(
        ucd_parse::parse::<_, ucd_parse::UnicodeData>(&UNICODE_DIRECTORY).unwrap(),
    ) {
        let general_category = if ["Nd", "Nl", "No"].contains(&row.general_category.as_str()) {
            "N"
        } else {
            row.general_category.as_str()
        };
        if let Some(name) = PROPERTIES.iter().find(|prop| **prop == general_category) {
            properties
                .entry(*name)
                .or_insert_with(Vec::new)
                .push(Codepoints::Single(row.codepoint));
        }

        if let Some(mapped) = row.simple_lowercase_mapping {
            if mapped != row.codepoint {
                to_lower.insert(row.codepoint.value(), (mapped.value(), 0, 0));
            }
        }
        if let Some(mapped) = row.simple_uppercase_mapping {
            if mapped != row.codepoint {
                to_upper.insert(row.codepoint.value(), (mapped.value(), 0, 0));
            }
        }
    }

    for row in ucd_parse::parse::<_, ucd_parse::SpecialCaseMapping>(&UNICODE_DIRECTORY).unwrap() {
        if !row.conditions.is_empty() {
            // Skip conditional case mappings
            continue;
        }

        let key = row.codepoint.value();
        if let Some(lower) = to_mapping(key, row.lowercase) {
            to_lower.insert(key, lower);
        }
        if let Some(upper) = to_mapping(key, row.uppercase) {
            to_upper.insert(key, upper);
        }
    }

    let mut properties: HashMap<&'static str, Vec<Range<u32>>> = properties
        .into_iter()
        .map(|(k, v)| {
            (
                k,
                v.into_iter()
                    .flat_map(|codepoints| match codepoints {
                        Codepoints::Single(c) => c
                            .scalar()
                            .map(|ch| (ch as u32..ch as u32 + 1))
                            .into_iter()
                            .collect::<Vec<_>>(),
                        Codepoints::Range(c) => c
                            .into_iter()
                            .flat_map(|c| c.scalar().map(|ch| (ch as u32..ch as u32 + 1)))
                            .collect::<Vec<_>>(),
                    })
                    .collect::<Vec<Range<u32>>>(),
            )
        })
        .collect();

    for ranges in properties.values_mut() {
        merge_ranges(ranges);
    }

    let mut properties = properties.into_iter().collect::<Vec<_>>();
    properties.sort_by_key(|p| p.0);
    UnicodeData { ranges: properties, to_lower, to_upper }
}

fn main() {
    let write_location = std::env::args().nth(1).unwrap_or_else(|| {
        eprintln!("Must provide path to write unicode tables to");
        eprintln!(
            "e.g. {} src/libcore/unicode/unicode_data.rs",
            std::env::args().next().unwrap_or_default()
        );
        std::process::exit(1);
    });

    // Optional test path, which is a Rust source file testing that the unicode
    // property lookups are correct.
    let test_path = std::env::args().nth(2);

    let unicode_data = load_data();
    let ranges_by_property = &unicode_data.ranges;

    if let Some(path) = test_path {
        std::fs::write(&path, generate_tests(&write_location, &ranges_by_property)).unwrap();
    }

    let mut total_bytes = 0;
    let mut modules = Vec::new();
    for (property, ranges) in ranges_by_property {
        let datapoints = ranges.iter().map(|r| r.end - r.start).sum::<u32>();
        let mut emitter = RawEmitter::new();
        emit_codepoints(&mut emitter, &ranges);

        modules.push((property.to_lowercase().to_string(), emitter.file));
        println!("{:15}: {} bytes, {} codepoints", property, emitter.bytes_used, datapoints,);
        total_bytes += emitter.bytes_used;
    }

    let mut table_file = String::new();

    table_file.push_str(
        "///! This file is generated by src/tools/unicode-table-generator; do not edit manually!\n",
    );

    table_file.push_str("use super::range_search;\n\n");

    table_file.push_str(&version());

    table_file.push('\n');

    modules.push((String::from("conversions"), case_mapping::generate_case_mapping(&unicode_data)));

    for (name, contents) in modules {
        table_file.push_str("#[rustfmt::skip]\n");
        table_file.push_str(&format!("pub mod {} {{\n", name));
        for line in contents.lines() {
            if !line.trim().is_empty() {
                table_file.push_str("    ");
                table_file.push_str(&line);
            }
            table_file.push('\n');
        }
        table_file.push_str("}\n\n");
    }

    std::fs::write(&write_location, format!("{}\n", table_file.trim_end())).unwrap();

    println!("Total table sizes: {} bytes", total_bytes);
}

fn version() -> String {
    let mut out = String::new();
    out.push_str("pub const UNICODE_VERSION: (u32, u32, u32) = ");

    let readme =
        std::fs::read_to_string(std::path::Path::new(UNICODE_DIRECTORY).join("ReadMe.txt"))
            .unwrap();

    let prefix = "for Version ";
    let start = readme.find(prefix).unwrap() + prefix.len();
    let end = readme.find(" of the Unicode Standard.").unwrap();
    let version =
        readme[start..end].split('.').map(|v| v.parse::<u32>().expect(&v)).collect::<Vec<_>>();
    let [major, minor, micro] = [version[0], version[1], version[2]];

    out.push_str(&format!("({}, {}, {});\n", major, minor, micro));
    out
}

fn fmt_list<V: std::fmt::Debug>(values: impl IntoIterator<Item = V>) -> String {
    let pieces = values.into_iter().map(|b| format!("{:?}, ", b)).collect::<Vec<_>>();
    let mut out = String::new();
    let mut line = format!("\n    ");
    for piece in pieces {
        if line.len() + piece.len() < 98 {
            line.push_str(&piece);
        } else {
            out.push_str(line.trim_end());
            out.push('\n');
            line = format!("    {}", piece);
        }
    }
    out.push_str(line.trim_end());
    out.push('\n');
    out
}

fn generate_tests(data_path: &str, ranges: &[(&str, Vec<Range<u32>>)]) -> String {
    let mut s = String::new();
    s.push_str("#![allow(incomplete_features, unused)]\n");
    s.push_str("#![feature(const_generics)]\n\n");
    s.push_str(&format!("#[path = \"{}\"]\n", data_path));
    s.push_str("mod unicode_data;\n\n");

    s.push_str(
        "
#[inline(always)]
fn range_search<const N: usize, const CHUNK_SIZE: usize, const N1: usize, const N2: usize>(
    needle: u32,
    chunk_idx_map: &[u8; N],
    (last_chunk_idx, last_chunk_mapping): (u16, u8),
    bitset_chunk_idx: &[[u8; CHUNK_SIZE]; N1],
    bitset: &[u64; N2],
) -> bool {
    let bucket_idx = (needle / 64) as usize;
    let chunk_map_idx = bucket_idx / CHUNK_SIZE;
    let chunk_piece = bucket_idx % CHUNK_SIZE;
    let chunk_idx = if chunk_map_idx >= N {
        if chunk_map_idx == last_chunk_idx as usize {
            last_chunk_mapping
        } else {
            return false;
        }
    } else {
        chunk_idx_map[chunk_map_idx]
    };
    let idx = bitset_chunk_idx[(chunk_idx as usize)][chunk_piece];
    let word = bitset[(idx as usize)];
    (word & (1 << (needle % 64) as u64)) != 0
}
    ",
    );

    s.push_str("\nfn main() {\n");

    for (property, ranges) in ranges {
        s.push_str(&format!(r#"    println!("Testing {}");"#, property));
        s.push('\n');
        s.push_str(&format!("    {}();\n", property.to_lowercase()));
        let mut is_true = Vec::new();
        let mut is_false = Vec::new();
        for ch_num in 0..(std::char::MAX as u32) {
            if std::char::from_u32(ch_num).is_none() {
                continue;
            }
            if ranges.iter().any(|r| r.contains(&ch_num)) {
                is_true.push(ch_num);
            } else {
                is_false.push(ch_num);
            }
        }

        s.push_str(&format!("    fn {}() {{\n", property.to_lowercase()));
        generate_asserts(&mut s, property, &is_true, true);
        generate_asserts(&mut s, property, &is_false, false);
        s.push_str("    }\n\n");
    }

    s.push_str("}");
    s
}

fn generate_asserts(s: &mut String, property: &str, points: &[u32], truthy: bool) {
    for range in ranges_from_set(points) {
        if range.end == range.start + 1 {
            s.push_str(&format!(
                "        assert!({}unicode_data::{}::lookup(std::char::from_u32({}).unwrap()), \"{}\");\n",
                if truthy { "" } else { "!" },
                property.to_lowercase(),
                range.start,
                std::char::from_u32(range.start).unwrap(),
        ));
        } else {
            s.push_str(&format!("        for chn in {:?}u32 {{\n", range));
            s.push_str(&format!(
                "            assert!({}unicode_data::{}::lookup(std::char::from_u32(chn).unwrap()), \"{{:?}}\", chn);\n",
                if truthy { "" } else { "!" },
                property.to_lowercase(),
        ));
            s.push_str("        }\n");
        }
    }
}

fn ranges_from_set(set: &[u32]) -> Vec<Range<u32>> {
    let mut ranges = set.iter().map(|e| (*e)..(*e + 1)).collect::<Vec<Range<u32>>>();
    merge_ranges(&mut ranges);
    ranges
}

fn merge_ranges(ranges: &mut Vec<Range<u32>>) {
    loop {
        let mut new_ranges = Vec::new();
        let mut idx_iter = 0..(ranges.len() - 1);
        while let Some(idx) = idx_iter.next() {
            let cur = ranges[idx].clone();
            let next = ranges[idx + 1].clone();
            if cur.end == next.start {
                let _ = idx_iter.next(); // skip next as we're merging it in
                new_ranges.push(cur.start..next.end);
            } else {
                new_ranges.push(cur);
            }
        }
        new_ranges.push(ranges.last().unwrap().clone());
        if new_ranges.len() == ranges.len() {
            *ranges = new_ranges;
            break;
        } else {
            *ranges = new_ranges;
        }
    }
}
<|endoftext|>


<commit_before>
extern crate mmap;
extern crate time;
extern crate timely;
extern crate dataflow_join;

use std::rc::Rc;

use dataflow_join::*;
use dataflow_join::graph::{GraphTrait, GraphMMap, GraphExtenderExt};

use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::progress::timestamp::RootTimestamp;

fn main () {

    let filename = std::env::args().nth(1).unwrap();
    let inspect = std::env::args().nth(2).unwrap() == "inspect";
    let step_size = std::env::args().nth(3).unwrap().parse::<usize>().unwrap();

    timely::execute_from_args(std::env::args().skip(4), move |root| {

        let index = root.index();
        let peers = root.peers();

        let graph = Rc::new(GraphMMap::<u32>::new(&filename));

        let (mut input, probe) = root.dataflow(|builder| {

            let (input, cliques) = builder.new_input::<u32>();

            // pairs
            let cliques = cliques.extend(vec![&graph.extend_using(|&a| a as u64)]);

            // triangles
            let cliques = cliques.flat_map(|(p, es)| es.into_iter().map(move |e| (p, e)))
                                 .extend(vec![&graph.extend_using(|&(a,_)| a as u64),
                                              &graph.extend_using(|&(_,b)| b as u64)]);

            // // quadrangles?
            // let cliques = cliques.flat_map(|(p,es)| es.into_iter().map(move |e| (p, e)))
            //                      .extend(vec![&graph.extend_using(|&((a,_),_)| a as u64),
            //                                   &graph.extend_using(|&((_,b),_)| b as u64),
            //                                   &graph.extend_using(|&((_,_),c)| c as u64)]);

            // // 5 cliques?
            // let cliques = cliques.flat_map(|(p,es)| es.into_iter().map(move |e| (p, e)))
            //                      .extend(vec![&graph.extend_using(|&(((a,_),_),_)| a as u64),
            //                                   &graph.extend_using(|&(((_,b),_),_)| b as u64),
            //                                   &graph.extend_using(|&(((_,_),c),_)| c as u64),
            //                                   &graph.extend_using(|&(((_,_),_),d)| d as u64)]);

            let mut count = 0;
            if inspect {
                cliques.inspect_batch(move |_t, b| {
                    for x in b { count += x.1.len(); }
                    println!("count: {}", count);
                });
            }

            (input, cliques.probe())
        });

        let nodes = graph.nodes() - 1;
        let limit = (nodes / step_size) + 1;
        for round in 0..limit {
            for source in 0..step_size {
                let candidate = source + round * step_size;
                if candidate % peers == index && candidate < nodes {
                    input.send(candidate as u32);
                }
            }

            input.advance_to(round as u64 + 1);
            root.step();
            while probe.less_than(&RootTimestamp::new((round - ::std::cmp::min(1, round)) as u64)) {
                root.step();
            }
        }
    }).unwrap();
}
<commit_msg>co-locate all triangles before counting<commit_after>
extern crate mmap;
extern crate time;
extern crate timely;
extern crate dataflow_join;

use std::rc::Rc;

use dataflow_join::*;
use dataflow_join::graph::{GraphTrait, GraphMMap, GraphExtenderExt};

use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::progress::timestamp::RootTimestamp;

fn main () {

    let filename = std::env::args().nth(1).unwrap();
    let inspect = std::env::args().nth(2).unwrap() == "inspect";
    let step_size = std::env::args().nth(3).unwrap().parse::<usize>().unwrap();

    timely::execute_from_args(std::env::args().skip(4), move |root| {

        let index = root.index();
        let peers = root.peers();

        let graph = Rc::new(GraphMMap::<u32>::new(&filename));

        let (mut input, probe) = root.dataflow(|builder| {

            let (input, cliques) = builder.new_input::<u32>();

            // pairs
            let cliques = cliques.extend(vec![&graph.extend_using(|&a| a as u64)]);

            // triangles
            let cliques = cliques.flat_map(|(p, es)| es.into_iter().map(move |e| (p, e)))
                                 .extend(vec![&graph.extend_using(|&(a,_)| a as u64),
                                              &graph.extend_using(|&(_,b)| b as u64)]);

            // // quadrangles?
            // let cliques = cliques.flat_map(|(p,es)| es.into_iter().map(move |e| (p, e)))
            //                      .extend(vec![&graph.extend_using(|&((a,_),_)| a as u64),
            //                                   &graph.extend_using(|&((_,b),_)| b as u64),
            //                                   &graph.extend_using(|&((_,_),c)| c as u64)]);

            // // 5 cliques?
            // let cliques = cliques.flat_map(|(p,es)| es.into_iter().map(move |e| (p, e)))
            //                      .extend(vec![&graph.extend_using(|&(((a,_),_),_)| a as u64),
            //                                   &graph.extend_using(|&(((_,b),_),_)| b as u64),
            //                                   &graph.extend_using(|&(((_,_),c),_)| c as u64),
            //                                   &graph.extend_using(|&(((_,_),_),d)| d as u64)]);

            let mut count = 0;
            if inspect {
                cliques
                    .exchange(|x| 0)
                    .inspect_batch(move |_t, b| {
                        for x in b { count += x.1.len(); }
                        println!("count: {}", count);
                    });
            }

            (input, cliques.probe())
        });

        let nodes = graph.nodes() - 1;
        let limit = (nodes / step_size) + 1;
        for round in 0..limit {
            for source in 0..step_size {
                let candidate = source + round * step_size;
                if candidate % peers == index && candidate < nodes {
                    input.send(candidate as u32);
                }
            }

            input.advance_to(round as u64 + 1);
            root.step();
            while probe.less_than(&RootTimestamp::new((round - ::std::cmp::min(1, round)) as u64)) {
                root.step();
            }
        }
    }).unwrap();
}
<|endoftext|>


<commit_before>use libimagrt::runtime::Runtime;

pub fn list(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("list")
        .map(|scmd| {
            debug!("Found 'list' subcommand...");

        });
}
<commit_msg>Implement list() for imag-counter<commit_after>use libimagrt::runtime::Runtime;
use libimagutil::trace::trace_error;
use libimagcounter::counter::Counter;
use libimagcounter::result::Result;

pub fn list(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("list")
        .map(|scmd| {
            debug!("Found 'list' subcommand...");

            Counter::all_counters(rt.store()).map(|iterator| {
                for counter in iterator {
                    counter.map(|c| {
                        let name    = c.name();
                        let value   = c.value();

                        if name.is_err() {
                            trace_error(&name.err().unwrap());
                        } else {

                            if value.is_err() {
                                trace_error(&value.err().unwrap());
                            } else {
                                println!("{} - {}", name.unwrap(), value.unwrap());
                            }
                        }
                    })
                    .map_err(|e| trace_error(&e));
                }
            })
            .map_err(|e| trace_error(&e))

        });
}
<|endoftext|>


<commit_before>use arc = std::arc;
use arc::ARC;
use au = gfx::geometry;
use font::{RunMetrics, Font};
use font_cache::FontCache;
use geom::point::Point2D;
use geom::size::Size2D;
use gfx::geometry::au;
use glyph::GlyphStore;
use layout::context::LayoutContext;
use libc::{c_void};
use servo_util::color;
use shaper::shape_textrun;
use std::arc;

pub struct TextRun {
    text: ~str,
    font: @Font,
    priv glyphs: GlyphStore,
}

// This is a hack until TextRuns are normally sendable, or
// we instead use ARC<TextRun> everywhere.
pub struct SendableTextRun {
    text: ~str,
    font_descriptor: (),
    priv glyphs: GlyphStore,
}

pub fn serialize(_cache: @FontCache, run: &TextRun) -> ~SendableTextRun {
    ~SendableTextRun {
        text: copy run.text,
        // TODO: actually serialize a font descriptor thingy
        font_descriptor: (),
        glyphs: copy run.glyphs,
    }
}

pub fn deserialize(cache: @FontCache, run: &SendableTextRun) -> @TextRun {
    @TextRun {
        text: copy run.text,
        // TODO: actually deserialize a font descriptor thingy
        font: cache.get_test_font(),
        glyphs: copy run.glyphs
    }
}

trait TextRunMethods {
    pure fn glyphs(&self) -> &self/GlyphStore;
    pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool);
    // TODO: needs to take box style as argument, or move to TextBox.
    // see Gecko's IsTrimmableSpace methods for details.
    pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool;

    fn metrics_for_range(offset: uint, length: uint) -> RunMetrics;
    fn min_width_for_range(offset: uint, length: uint) -> au;
    fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool);
}

impl TextRun : TextRunMethods {
    pure fn glyphs(&self) -> &self/GlyphStore { &self.glyphs }

    pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool {
        let mut i = offset;
        while i < offset + length {
            let {ch, next} = str::char_range_at(self.text, i);
            match ch {
                ' ' | '\t' | '\r'  => {},
                _ => { return false; }
            }
            i = next;
        }
        return true;
    }

    fn metrics_for_range(offset: uint, length: uint) -> RunMetrics {
        self.font.measure_text(&self, offset, length)
    }

    fn min_width_for_range(offset: uint, length: uint) -> au {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut max_piece_width = au(0);
        for self.iter_indivisible_pieces_for_range(offset, length) |piece_offset, piece_len| {
            let metrics = self.font.measure_text(&self, piece_offset, piece_len);
            if metrics.advance_width > max_piece_width {
                max_piece_width = metrics.advance_width;
            }
        };

        return max_piece_width;
    }

    fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut clump_start = offset;
        let mut clump_end = offset;
        let mut in_clump = false;

        // clump non-linebreaks of nonzero length
        for uint::range(offset, offset + length) |i| {
            match (self.glyphs.char_is_newline(i), in_clump) {
                (false, true)  => { clump_end = i; }
                (false, false) => { in_clump = true; clump_start = i; clump_end = i; }
                (true, false) => { /* chomp whitespace */ }
                (true, true)  => {
                    in_clump = false;
                    // don't include the linebreak 'glyph'
                    // (we assume there's one GlyphEntry for a newline, and no actual glyphs)
                    if !f(clump_start, clump_end - clump_start + 1) { break }
                }
            }
        }

        // flush any remaining chars as a line
        if in_clump {
            clump_end = offset + length - 1;
            f(clump_start, clump_end - clump_start + 1);
        }
    }

    pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut clump_offset = offset;
        let mut clump_length;

        loop {
            // find next non-whitespace byte index, then clump all whitespace before it.
            match str::find_from(self.text, clump_offset, |c| !char::is_whitespace(c)) {
                Some(nonws_char_offset) => {
                    clump_length = nonws_char_offset - clump_offset;
                    if !f(clump_offset, clump_length) { break }
                    clump_offset += clump_length;
                    // reached end
                    if clump_offset == offset + length { break }
                },
                None => {
                    // nothing left, flush last piece containing only whitespace
                    if clump_offset < offset + length {
                        let clump_length = (offset + length) - clump_offset;
                        f(clump_offset, clump_length);
                    }
                    break
                }
            };

            // find next whitespace byte index, then clump all non-whitespace before it.
            match str::find_from(self.text, clump_offset, |c| char::is_whitespace(c)) {
                Some(ws_char_offset) => {
                    clump_length = ws_char_offset - clump_offset;
                    if !f(clump_offset, clump_length) { break }
                    clump_offset += clump_length;
                    // reached end
                    if clump_offset == offset + length { break }
                }
                None => {
                    // nothing left, flush last piece containing only non-whitespaces
                    if clump_offset < offset + length {
                        let clump_length = (offset + length) - clump_offset;
                        f(clump_offset, clump_length);
                    }
                    break
                }
            }
        }
    }
}

fn TextRun(font: @Font, text: ~str) -> TextRun {
    let glyph_store = GlyphStore(text.len());
    let run = TextRun {
        text: text,
        font: font,
        glyphs: glyph_store,
    };

    shape_textrun(&run);
    return run;
}

// this test can't run until LayoutContext is removed as an argument
// to min_width_for_range.
/*
#[test]
fn test_calc_min_break_width() {

    fn test_min_width_for_run(text: ~str, width: au) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, text);
        run.min_width_for_range(0, text.len())
    }

    test_min_width_for_run(~"firecracker", au::from_px(84));
    test_min_width_for_run(~"firecracker yumyum", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker yumyum", au::from_px(84));
}
*/

#[test]
#[ignore]
fn test_iter_indivisible_pieces() {
    fn test_pieces(text: ~str, res: ~[~str]) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, copy text);
        let mut slices : ~[~str] = ~[];
        for run.iter_indivisible_pieces_for_range(0, text.len()) |offset, length| {
            slices.push(str::slice(text, offset, length));
        }
        assert slices == res;
    }

    test_pieces(~"firecracker yumyum woopwoop", ~[~"firecracker", ~" ", ~"yumyum", ~" ", ~"woopwoop"]);
    test_pieces(~"firecracker yumyum     ", ~[~"firecracker", ~" ", ~"yumyum", ~"     "]);
    test_pieces(~"     firecracker yumyum", ~[~"     ", ~"firecracker", ~" ", ~"yumyum"]);
    test_pieces(~"  ", ~[~"  "]);
    test_pieces(~"", ~[]);
}

<commit_msg>Convert TextRun::iter_natural_lines_for_range to use spans. Fixes #123.<commit_after>use arc = std::arc;
use arc::ARC;
use au = gfx::geometry;
use font::{RunMetrics, Font};
use font_cache::FontCache;
use geom::point::Point2D;
use geom::size::Size2D;
use gfx::geometry::au;
use glyph::GlyphStore;
use layout::context::LayoutContext;
use libc::{c_void};
use servo_util::color;
use shaper::shape_textrun;
use std::arc;

pub struct TextRun {
    text: ~str,
    font: @Font,
    priv glyphs: GlyphStore,
}

// This is a hack until TextRuns are normally sendable, or
// we instead use ARC<TextRun> everywhere.
pub struct SendableTextRun {
    text: ~str,
    font_descriptor: (),
    priv glyphs: GlyphStore,
}

pub fn serialize(_cache: @FontCache, run: &TextRun) -> ~SendableTextRun {
    ~SendableTextRun {
        text: copy run.text,
        // TODO: actually serialize a font descriptor thingy
        font_descriptor: (),
        glyphs: copy run.glyphs,
    }
}

pub fn deserialize(cache: @FontCache, run: &SendableTextRun) -> @TextRun {
    @TextRun {
        text: copy run.text,
        // TODO: actually deserialize a font descriptor thingy
        font: cache.get_test_font(),
        glyphs: copy run.glyphs
    }
}

trait TextRunMethods {
    pure fn glyphs(&self) -> &self/GlyphStore;
    pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool);
    // TODO: needs to take box style as argument, or move to TextBox.
    // see Gecko's IsTrimmableSpace methods for details.
    pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool;

    fn metrics_for_range(offset: uint, length: uint) -> RunMetrics;
    fn min_width_for_range(offset: uint, length: uint) -> au;
    fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool);
}

impl TextRun : TextRunMethods {
    pure fn glyphs(&self) -> &self/GlyphStore { &self.glyphs }

    pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool {
        let mut i = offset;
        while i < offset + length {
            let {ch, next} = str::char_range_at(self.text, i);
            match ch {
                ' ' | '\t' | '\r'  => {},
                _ => { return false; }
            }
            i = next;
        }
        return true;
    }

    fn metrics_for_range(offset: uint, length: uint) -> RunMetrics {
        self.font.measure_text(&self, offset, length)
    }

    fn min_width_for_range(offset: uint, length: uint) -> au {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut max_piece_width = au(0);
        for self.iter_indivisible_pieces_for_range(offset, length) |piece_offset, piece_len| {
            let metrics = self.font.measure_text(&self, piece_offset, piece_len);
            if metrics.advance_width > max_piece_width {
                max_piece_width = metrics.advance_width;
            }
        };

        return max_piece_width;
    }

    fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut clump_offset = offset;
        let mut clump_length = 0;
        let mut in_clump = false;

        // clump non-linebreaks of nonzero length
        for uint::range(offset, offset + length) |i| {
            match (self.glyphs.char_is_newline(i), in_clump) {
                (false, true)  => { clump_length += 1; }
                (false, false) => { in_clump = true; clump_offset = i; clump_length = 1; }
                (true, false) => { /* chomp whitespace */ }
                (true, true)  => {
                    in_clump = false;
                    // don't include the linebreak 'glyph'
                    // (we assume there's one GlyphEntry for a newline, and no actual glyphs)
                    if !f(clump_offset, clump_length) { break }
                }
            }
        }

        // flush any remaining chars as a line
        if in_clump {
            clump_length = (offset + length) - clump_offset;
            f(clump_offset, clump_length);
        }
    }

    pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) {
        assert length > 0;
        assert offset < self.text.len();
        assert offset + length <= self.text.len();

        let mut clump_offset = offset;
        let mut clump_length;

        loop {
            // find next non-whitespace byte index, then clump all whitespace before it.
            match str::find_from(self.text, clump_offset, |c| !char::is_whitespace(c)) {
                Some(nonws_char_offset) => {
                    clump_length = nonws_char_offset - clump_offset;
                    if !f(clump_offset, clump_length) { break }
                    clump_offset += clump_length;
                    // reached end
                    if clump_offset == offset + length { break }
                },
                None => {
                    // nothing left, flush last piece containing only whitespace
                    if clump_offset < offset + length {
                        let clump_length = (offset + length) - clump_offset;
                        f(clump_offset, clump_length);
                    }
                    break
                }
            };

            // find next whitespace byte index, then clump all non-whitespace before it.
            match str::find_from(self.text, clump_offset, |c| char::is_whitespace(c)) {
                Some(ws_char_offset) => {
                    clump_length = ws_char_offset - clump_offset;
                    if !f(clump_offset, clump_length) { break }
                    clump_offset += clump_length;
                    // reached end
                    if clump_offset == offset + length { break }
                }
                None => {
                    // nothing left, flush last piece containing only non-whitespaces
                    if clump_offset < offset + length {
                        let clump_length = (offset + length) - clump_offset;
                        f(clump_offset, clump_length);
                    }
                    break
                }
            }
        }
    }
}

fn TextRun(font: @Font, text: ~str) -> TextRun {
    let glyph_store = GlyphStore(text.len());
    let run = TextRun {
        text: text,
        font: font,
        glyphs: glyph_store,
    };

    shape_textrun(&run);
    return run;
}

// this test can't run until LayoutContext is removed as an argument
// to min_width_for_range.
/*
#[test]
fn test_calc_min_break_width() {

    fn test_min_width_for_run(text: ~str, width: au) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, text);
        run.min_width_for_range(0, text.len())
    }

    test_min_width_for_run(~"firecracker", au::from_px(84));
    test_min_width_for_run(~"firecracker yumyum", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker", au::from_px(84));
    test_min_width_for_run(~"yumyum firecracker yumyum", au::from_px(84));
}
*/

#[test]
#[ignore]
fn test_iter_indivisible_pieces() {
    fn test_pieces(text: ~str, res: ~[~str]) {
        let flib = FontCache();
        let font = flib.get_test_font();
        let run = TextRun(font, copy text);
        let mut slices : ~[~str] = ~[];
        for run.iter_indivisible_pieces_for_range(0, text.len()) |offset, length| {
            slices.push(str::slice(text, offset, length));
        }
        assert slices == res;
    }

    test_pieces(~"firecracker yumyum woopwoop", ~[~"firecracker", ~" ", ~"yumyum", ~" ", ~"woopwoop"]);
    test_pieces(~"firecracker yumyum     ", ~[~"firecracker", ~" ", ~"yumyum", ~"     "]);
    test_pieces(~"     firecracker yumyum", ~[~"     ", ~"firecracker", ~" ", ~"yumyum"]);
    test_pieces(~"  ", ~[~"  "]);
    test_pieces(~"", ~[]);
}

<|endoftext|>


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

#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
use serialize::json::{Json, ToJson};
#[cfg(feature = "serde_type")]
use serde_json::value::{self, Value as Json};

use helpers::HelperDef;
use registry::Registry;
use context::{Context, JsonTruthy};
use render::{Renderable, RenderContext, RenderError, Helper};

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

impl HelperDef for EachHelper {
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let value = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"each\"")));

        let template = h.template();

        match template {
            Some(t) => {
                rc.promote_local_vars();
                if let Some(path_root) = value.path_root() {
                    let local_path_root = format!("{}/{}", rc.get_path(), path_root);
                    rc.set_local_path_root(local_path_root);
                }

                debug!("each value {:?}", value.value());
                let rendered = match (value.value().is_truthy(), value.value()) {
                    (true, &Json::Array(ref list)) => {
                        let len = list.len();
                        for i in 0..len {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), (i == 0usize).to_json());
                            local_rc.set_local_var("@last".to_string(), (i == len - 1).to_json());
                            local_rc.set_local_var("@index".to_string(), i.to_json());

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       i);
                                debug!("each path {:?}", new_path);
                                local_rc.set_path(new_path.clone());
                            }

                            if let Some(block_param) = h.block_param() {
                                // need better impl
                                let mut map = BTreeMap::new();
                                map.insert(block_param.to_string(), list[i].to_json());
                                local_rc.push_block_context(&map);
                            }

                            try!(t.render(c, r, &mut local_rc));

                            if h.block_param().is_some() {
                                local_rc.pop_block_context();
                            }
                        }
                        Ok(())
                    }
                    (true, &Json::Object(ref obj)) => {
                        let mut first: bool = true;
                        for k in obj.keys() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), first.to_json());
                            if first {
                                first = false;
                            }

                            local_rc.set_local_var("@key".to_string(), k.to_json());

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       k);
                                local_rc.set_path(new_path);
                            }

                            try!(t.render(c, r, &mut local_rc));
                        }

                        Ok(())
                    }
                    (false, _) => {
                        if let Some(else_template) = h.inverse() {
                            try!(else_template.render(c, r, rc));
                        }
                        Ok(())
                    }
                    _ => {
                        Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
                    }
                };

                rc.demote_local_vars();
                rendered
            }
            None => Ok(()),
        }
    }

    #[cfg(feature = "serde_type")]
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let value = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"each\"")));

        let template = h.template();

        match template {
            Some(t) => {
                rc.promote_local_vars();
                if let Some(path_root) = value.path_root() {
                    let local_path_root = format!("{}/{}", rc.get_path(), path_root);
                    rc.set_local_path_root(local_path_root);
                }

                debug!("each value {:?}", value.value());
                let rendered = match (value.value().is_truthy(), value.value()) {
                    (true, &Json::Array(ref list)) => {
                        let len = list.len();
                        for i in 0..len {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(),
                                                   value::to_value(&(i == 0usize)));
                            local_rc.set_local_var("@last".to_string(),
                                                   value::to_value(&(i == len - 1)));
                            local_rc.set_local_var("@index".to_string(), value::to_value(&i));

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       i);
                                debug!("each value {:?}", new_path);
                                local_rc.set_path(new_path);
                            }
                            try!(t.render(c, r, &mut local_rc));
                        }
                        Ok(())
                    }
                    (true, &Json::Object(ref obj)) => {
                        let mut first: bool = true;
                        for k in obj.keys() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), value::to_value(&first));
                            if first {
                                first = false;
                            }

                            local_rc.set_local_var("@key".to_string(), value::to_value(&k));
                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       k);
                                debug!("each value {:?}", new_path);
                                local_rc.set_path(new_path);
                            }

                            try!(t.render(c, r, &mut local_rc));
                        }

                        Ok(())
                    }
                    (false, _) => {
                        if let Some(else_template) = h.inverse() {
                            try!(else_template.render(c, r, rc));
                        }
                        Ok(())
                    }
                    _ => {
                        Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
                    }
                };
                // rc.set_path(path);
                rc.demote_local_vars();
                rendered
            }
            None => Ok(()),
        }
    }
}

pub static EACH_HELPER: EachHelper = EachHelper;

#[cfg(test)]
mod test {
    use template::Template;
    use registry::Registry;

    use std::collections::BTreeMap;

    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    use serialize::json::Json;

    #[test]
    fn test_each() {
        let t0 = Template::compile("{{#each this}}{{@first}}|{{@last}}|{{@index}}:\
                                    {{this}}|{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t1 = Template::compile("{{#each this}}{{@first}}|{{@key}}:{{this}}|{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);

        let r0 = handlebars.render("t0", &vec![1u16, 2u16, 3u16]);
        assert_eq!(r0.ok().unwrap(),
                   "true|false|0:1|false|false|1:2|false|true|2:3|".to_string());

        let mut m: BTreeMap<String, u16> = BTreeMap::new();
        m.insert("ftp".to_string(), 21);
        m.insert("http".to_string(), 80);
        let r1 = handlebars.render("t1", &m);
        assert_eq!(r1.ok().unwrap(), "true|ftp:21|false|http:80|".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_each_with_parent() {

        let json_str = r#"{"a":{"a":99,"c":[{"d":100},{"d":200}]}}"#;

        let data = Json::from_str(json_str).unwrap();
        // println!("data: {}", data);

        // previously, to access the parent in an each block,
        // a user would need to specify ../../b, as the path
        // that is computed includes the array index: ./a.c.[0]
        let t0 = Template::compile("{{#each a.c}} d={{d}} b={{../a.a}} {{/each}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), " d=100 b=99  d=200 b=99 ".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_nested_each_with_parent() {

        let json_str = r#"{"a": [{"b": [{"d": 100}], "c": 200}]}"#;

        let data = Json::from_str(json_str).unwrap();
        let t0 = Template::compile("{{#each a}}{{#each b}}{{d}}:{{../c}}{{/each}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), "100:200".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_nested_each() {

        let json_str = r#"{"a": [{"b": true}], "b": [[1, 2, 3],[4, 5]]}"#;

        let data = Json::from_str(json_str).unwrap();
        let t0 = Template::compile("{{#each b}}{{#if ../a}}{{#each this}}{{this}}{{/each}}{{/if}}{{/each}}".to_string())
            .ok()
            .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), "12345".to_string());
    }

    #[test]
    fn test_nested_array() {
        let t0 = Template::compile("{{#each this.[0]}}{{this}}{{/each}}".to_owned()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r0 = handlebars.render("t0", &(vec![vec![1, 2, 3]]));

        assert_eq!(r0.ok().unwrap(), "123".to_string());
    }

    #[test]
    fn test_empty_key() {
        let t0 = Template::compile("{{#each this}}{{@key}}-{{value}}\n{{/each}}".to_owned())
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r0 = handlebars.render("t0",
                                   &({
                                       let mut rv = BTreeMap::new();
                                       rv.insert("foo".to_owned(), {
                                           let mut rv = BTreeMap::new();
                                           rv.insert("value".to_owned(), "bar".to_owned());
                                           rv
                                       });
                                       rv.insert("".to_owned(), {
                                           let mut rv = BTreeMap::new();
                                           rv.insert("value".to_owned(), "baz".to_owned());
                                           rv
                                       });
                                       rv
                                   }))
                           .unwrap();

        let mut r0_sp: Vec<_> = r0.split('\n').collect();
        r0_sp.sort();

        assert_eq!(r0_sp, vec!["", "-baz", "foo-bar"]);
    }

    #[test]
    fn test_each_else() {
        let t0 = Template::compile("{{#each a}}1{{else}}empty{{/each}}".to_owned()).unwrap();
        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        let m1 = btreemap! {
            "a".to_string() => Vec::<String>::new(),
        };
        let r0 = handlebars.render("t0", &m1).unwrap();
        assert_eq!(r0, "empty");

        let m2 = btreemap!{
            "b".to_string() => Vec::<String>::new()
        };
        let r1 = handlebars.render("t0", &m2).unwrap();
        assert_eq!(r1, "empty");
    }

    #[test]
    fn test_block_param() {
        let t0 = Template::compile("{{#each a as |i|}}{{i}}{{/each}}".to_owned()).unwrap();
        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        let m1 = btreemap! {
            "a".to_string() => vec![1,2,3,4,5]
        };
        let r0 = handlebars.render("t0", &m1).unwrap();
        println!("render: {}", r0);
        assert_eq!(r0, "12345");
    }

}
<commit_msg>(feat) block params for each block [#42]<commit_after>use std::collections::BTreeMap;

#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
use serialize::json::{Json, ToJson};
#[cfg(feature = "serde_type")]
use serde_json::value::{self, Value as Json};

use helpers::HelperDef;
use registry::Registry;
use context::{Context, JsonTruthy};
use render::{Renderable, RenderContext, RenderError, Helper};

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

impl HelperDef for EachHelper {
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let value = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"each\"")));

        let template = h.template();

        match template {
            Some(t) => {
                rc.promote_local_vars();
                if let Some(path_root) = value.path_root() {
                    let local_path_root = format!("{}/{}", rc.get_path(), path_root);
                    rc.set_local_path_root(local_path_root);
                }

                debug!("each value {:?}", value.value());
                let rendered = match (value.value().is_truthy(), value.value()) {
                    (true, &Json::Array(ref list)) => {
                        let len = list.len();
                        for i in 0..len {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), (i == 0usize).to_json());
                            local_rc.set_local_var("@last".to_string(), (i == len - 1).to_json());
                            local_rc.set_local_var("@index".to_string(), i.to_json());

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       i);
                                debug!("each path {:?}", new_path);
                                local_rc.set_path(new_path.clone());
                            }

                            if let Some(block_param) = h.block_param() {
                                let mut map = BTreeMap::new();
                                map.insert(block_param.to_string(), list[i].to_json());
                                local_rc.push_block_context(&map);
                            }

                            try!(t.render(c, r, &mut local_rc));

                            if h.block_param().is_some() {
                                local_rc.pop_block_context();
                            }
                        }
                        Ok(())
                    }
                    (true, &Json::Object(ref obj)) => {
                        let mut first: bool = true;
                        for k in obj.keys() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), first.to_json());
                            if first {
                                first = false;
                            }

                            local_rc.set_local_var("@key".to_string(), k.to_json());

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       k);
                                local_rc.set_path(new_path);
                            }

                            try!(t.render(c, r, &mut local_rc));
                            if let Some((bp_key, bp_val)) = h.block_param_pair() {
                                let mut map = BTreeMap::new();
                                map.insert(bp_key.to_string(), k.to_json());
                                map.insert(bp_val.to_string(), obj.get(k).unwrap().to_json());
                                local_rc.push_block_context(&map);
                            }

                            try!(t.render(c, r, &mut local_rc));

                            if h.block_param().is_some() {
                                local_rc.pop_block_context();
                            }
                        }

                        Ok(())
                    }
                    (false, _) => {
                        if let Some(else_template) = h.inverse() {
                            try!(else_template.render(c, r, rc));
                        }
                        Ok(())
                    }
                    _ => {
                        Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
                    }
                };

                rc.demote_local_vars();
                rendered
            }
            None => Ok(()),
        }
    }

    #[cfg(feature = "serde_type")]
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let value = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"each\"")));

        let template = h.template();

        match template {
            Some(t) => {
                rc.promote_local_vars();
                if let Some(path_root) = value.path_root() {
                    let local_path_root = format!("{}/{}", rc.get_path(), path_root);
                    rc.set_local_path_root(local_path_root);
                }

                debug!("each value {:?}", value.value());
                let rendered = match (value.value().is_truthy(), value.value()) {
                    (true, &Json::Array(ref list)) => {
                        let len = list.len();
                        for i in 0..len {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(),
                                                   value::to_value(&(i == 0usize)));
                            local_rc.set_local_var("@last".to_string(),
                                                   value::to_value(&(i == len - 1)));
                            local_rc.set_local_var("@index".to_string(), value::to_value(&i));

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       i);
                                debug!("each value {:?}", new_path);
                                local_rc.set_path(new_path);
                            }

                            if let Some(block_param) = h.block_param() {
                                let mut map = BTreeMap::new();
                                map.insert(block_param.to_string(), value::to_value(&list[i]));
                                local_rc.push_block_context(&map);
                            }

                            try!(t.render(c, r, &mut local_rc));

                            if h.block_param().is_some() {
                                local_rc.pop_block_context();
                            }
                        }
                        Ok(())
                    }
                    (true, &Json::Object(ref obj)) => {
                        let mut first: bool = true;
                        for k in obj.keys() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), value::to_value(&first));
                            if first {
                                first = false;
                            }

                            local_rc.set_local_var("@key".to_string(), value::to_value(&k));
                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       k);
                                debug!("each value {:?}", new_path);
                                local_rc.set_path(new_path);
                            }

                            if let Some((bp_key, bp_val)) = h.block_param_pair() {
                                let mut map = BTreeMap::new();
                                map.insert(bp_key.to_string(), value::to_value(&k));
                                map.insert(bp_val.to_string(),
                                           value::to_value(&obj.get(k).unwrap()));
                                local_rc.push_block_context(&map);
                            }

                            try!(t.render(c, r, &mut local_rc));

                            if h.block_param().is_some() {
                                local_rc.pop_block_context();
                            }
                        }

                        Ok(())
                    }
                    (false, _) => {
                        if let Some(else_template) = h.inverse() {
                            try!(else_template.render(c, r, rc));
                        }
                        Ok(())
                    }
                    _ => {
                        Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
                    }
                };
                // rc.set_path(path);
                rc.demote_local_vars();
                rendered
            }
            None => Ok(()),
        }
    }
}

pub static EACH_HELPER: EachHelper = EachHelper;

#[cfg(test)]
mod test {
    use template::Template;
    use registry::Registry;

    use std::collections::BTreeMap;

    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    use serialize::json::Json;

    #[test]
    fn test_each() {
        let t0 = Template::compile("{{#each this}}{{@first}}|{{@last}}|{{@index}}:\
                                    {{this}}|{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t1 = Template::compile("{{#each this}}{{@first}}|{{@key}}:{{this}}|{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);

        let r0 = handlebars.render("t0", &vec![1u16, 2u16, 3u16]);
        assert_eq!(r0.ok().unwrap(),
                   "true|false|0:1|false|false|1:2|false|true|2:3|".to_string());

        let mut m: BTreeMap<String, u16> = BTreeMap::new();
        m.insert("ftp".to_string(), 21);
        m.insert("http".to_string(), 80);
        let r1 = handlebars.render("t1", &m);
        assert_eq!(r1.ok().unwrap(), "true|ftp:21|false|http:80|".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_each_with_parent() {

        let json_str = r#"{"a":{"a":99,"c":[{"d":100},{"d":200}]}}"#;

        let data = Json::from_str(json_str).unwrap();
        // println!("data: {}", data);

        // previously, to access the parent in an each block,
        // a user would need to specify ../../b, as the path
        // that is computed includes the array index: ./a.c.[0]
        let t0 = Template::compile("{{#each a.c}} d={{d}} b={{../a.a}} {{/each}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), " d=100 b=99  d=200 b=99 ".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_nested_each_with_parent() {

        let json_str = r#"{"a": [{"b": [{"d": 100}], "c": 200}]}"#;

        let data = Json::from_str(json_str).unwrap();
        let t0 = Template::compile("{{#each a}}{{#each b}}{{d}}:{{../c}}{{/each}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), "100:200".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_nested_each() {

        let json_str = r#"{"a": [{"b": true}], "b": [[1, 2, 3],[4, 5]]}"#;

        let data = Json::from_str(json_str).unwrap();
        let t0 = Template::compile("{{#each b}}{{#if ../a}}{{#each this}}{{this}}{{/each}}{{/if}}{{/each}}".to_string())
            .ok()
            .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), "12345".to_string());
    }

    #[test]
    fn test_nested_array() {
        let t0 = Template::compile("{{#each this.[0]}}{{this}}{{/each}}".to_owned()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r0 = handlebars.render("t0", &(vec![vec![1, 2, 3]]));

        assert_eq!(r0.ok().unwrap(), "123".to_string());
    }

    #[test]
    fn test_empty_key() {
        let t0 = Template::compile("{{#each this}}{{@key}}-{{value}}\n{{/each}}".to_owned())
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r0 = handlebars.render("t0",
                                   &({
                                       let mut rv = BTreeMap::new();
                                       rv.insert("foo".to_owned(), {
                                           let mut rv = BTreeMap::new();
                                           rv.insert("value".to_owned(), "bar".to_owned());
                                           rv
                                       });
                                       rv.insert("".to_owned(), {
                                           let mut rv = BTreeMap::new();
                                           rv.insert("value".to_owned(), "baz".to_owned());
                                           rv
                                       });
                                       rv
                                   }))
                           .unwrap();

        let mut r0_sp: Vec<_> = r0.split('\n').collect();
        r0_sp.sort();

        assert_eq!(r0_sp, vec!["", "-baz", "foo-bar"]);
    }

    #[test]
    fn test_each_else() {
        let t0 = Template::compile("{{#each a}}1{{else}}empty{{/each}}".to_owned()).unwrap();
        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        let m1 = btreemap! {
            "a".to_string() => Vec::<String>::new(),
        };
        let r0 = handlebars.render("t0", &m1).unwrap();
        assert_eq!(r0, "empty");

        let m2 = btreemap!{
            "b".to_string() => Vec::<String>::new()
        };
        let r1 = handlebars.render("t0", &m2).unwrap();
        assert_eq!(r1, "empty");
    }

    #[test]
    fn test_block_param() {
        let t0 = Template::compile("{{#each a as |i|}}{{i}}{{/each}}".to_owned()).unwrap();
        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        let m1 = btreemap! {
            "a".to_string() => vec![1,2,3,4,5]
        };
        let r0 = handlebars.render("t0", &m1).unwrap();
        println!("render: {}", r0);
        assert_eq!(r0, "12345");
    }

}
<|endoftext|>


<commit_before>extern crate loader = "snowmew-loader";

use loader::Obj;

#[test]
fn load_teapot()
{
    let teapot = Obj::load(&Path::new("assets/teapot.obj"));

    assert!(teapot.is_some());

    let teapot = teapot.unwrap();

    for &(ref name, _, _) in teapot.objects.iter() {
        assert!(name.as_slice() == "Teapot01");
    }
}<commit_msg>fixed unit test<commit_after>extern crate loader = "snowmew-loader";

use loader::Obj;

#[test]
fn load_teapot()
{
    let teapot = Obj::load(&Path::new("assets/suzanne.obj"));

    assert!(teapot.is_some());

    let teapot = teapot.unwrap();

    for &(ref name, _, _) in teapot.objects.iter() {
        assert!(name.as_slice() == "Suzanne");
    }
}<|endoftext|>


<commit_before>//! Methods for reading animated characters out of anim.mul/anim.idx
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use color::{Color, Color16, Color32};
use mul_reader::MulReader;
use std::fs::{File};
use std::io::{Result, Error, ErrorKind, Cursor, SeekFrom, Seek, Read};
use std::path::Path;

pub struct Row {
    header: u16,
    offset: u16,
    image_data: Vec<u8>
}

pub struct AnimFrame {
    pub image_centre_x: u16,
    pub image_centre_y: u16,
    pub width: u16,
    pub height: u16,
    pub data: Vec<Row>
}

pub struct AnimGroup {
    pub palette: [Color16; 256],
    pub frame_count: u32,
    pub frames: Vec<AnimFrame>
}

pub struct AnimReader<T: Read + Seek> {
    mul_reader: MulReader<T>
}

fn read_frame<T: Read + Seek>(reader: &mut T) -> Result<AnimFrame> {
    let image_centre_x = try!(reader.read_u16::<LittleEndian>());
    let image_centre_y = try!(reader.read_u16::<LittleEndian>());
    let width = try!(reader.read_u16::<LittleEndian>());
    let height = try!(reader.read_u16::<LittleEndian>());
    // Read data
    Ok(AnimFrame {
        image_centre_x: image_centre_x,
        image_centre_y: image_centre_y,
        width: width,
        height: height,
        data: vec![]
    })
}

impl AnimReader<File> {

    pub fn new(index_path: &Path, mul_path: &Path) -> Result<AnimReader<File>> {
        let mul_reader = try!(MulReader::new(index_path, mul_path));
        Ok(AnimReader {
            mul_reader: mul_reader
        })
    }
}

impl <T: Read + Seek> AnimReader<T> {

    pub fn from_mul(reader: MulReader<T>) -> AnimReader<T> {
        AnimReader {
            mul_reader: reader
        }
    }

    pub fn read(&mut self, id: u32) -> Result<AnimGroup> {
        let raw = try!(self.mul_reader.read(id));
        let mut reader = Cursor::new(raw.data);
        // Read the palette
        let mut palette = [0; 256];
        for i in 0..256 {
            palette[i] = try!(reader.read_u16::<LittleEndian>());
        }

        let frame_count = try!(reader.read_u32::<LittleEndian>());
        let mut frame_offsets = vec![];
        for _ in 0..frame_count {
            frame_offsets.push(try!(reader.read_u32::<LittleEndian>()));
        }

        let mut frames = vec![];
        for offset in frame_offsets {
            try!(reader.seek(SeekFrom::Start((256 * 2 + offset) as u64)));
            frames.push(try!(read_frame(&mut reader)));
        }

        Ok(AnimGroup {
            palette: palette,
            frame_count: frame_count,
            frames: frames
        })
    }
}
<commit_msg>Correct size of types<commit_after>//! Methods for reading animated characters out of anim.mul/anim.idx
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use color::{Color, Color16, Color32};
use mul_reader::MulReader;
use std::fs::{File};
use std::io::{Result, Error, ErrorKind, Cursor, SeekFrom, Seek, Read};
use std::path::Path;

const PALETTE_SIZE: usize = 256;

pub struct Row {
    header: u16,
    offset: u16,
    image_data: Vec<u8>
}

pub struct AnimFrame {
    pub image_centre_x: i16,
    pub image_centre_y: i16,
    pub width: u16,
    pub height: u16,
    pub data: Vec<Row>
}

pub struct AnimGroup {
    pub palette: [Color16; 256],
    pub frame_count: u32,
    pub frames: Vec<AnimFrame>
}

pub struct AnimReader<T: Read + Seek> {
    mul_reader: MulReader<T>
}

fn read_frame<T: Read + Seek>(reader: &mut T) -> Result<AnimFrame> {
    let image_centre_x = try!(reader.read_i16::<LittleEndian>());
    let image_centre_y = try!(reader.read_i16::<LittleEndian>());
    let width = try!(reader.read_u16::<LittleEndian>());
    let height = try!(reader.read_u16::<LittleEndian>());
    // Read data
    Ok(AnimFrame {
        image_centre_x: image_centre_x,
        image_centre_y: image_centre_y,
        width: width,
        height: height,
        data: vec![]
    })
}

impl AnimReader<File> {

    pub fn new(index_path: &Path, mul_path: &Path) -> Result<AnimReader<File>> {
        let mul_reader = try!(MulReader::new(index_path, mul_path));
        Ok(AnimReader {
            mul_reader: mul_reader
        })
    }
}

impl <T: Read + Seek> AnimReader<T> {

    pub fn from_mul(reader: MulReader<T>) -> AnimReader<T> {
        AnimReader {
            mul_reader: reader
        }
    }

    pub fn read(&mut self, id: u32) -> Result<AnimGroup> {

        let raw = try!(self.mul_reader.read(id));
        let mut reader = Cursor::new(raw.data);
        // Read the palette
        let mut palette = [0; PALETTE_SIZE];
        for i in 0..PALETTE_SIZE {
            palette[i] = try!(reader.read_u16::<LittleEndian>());
        }

        let frame_count = try!(reader.read_u32::<LittleEndian>());
        let mut frame_offsets = vec![];
        for _ in 0..frame_count {
            frame_offsets.push(try!(reader.read_u32::<LittleEndian>()));
        }

        let mut frames = vec![];
        for offset in frame_offsets {
            try!(reader.seek(SeekFrom::Start((PALETTE_SIZE as u32 * 2 + offset) as u64)));
            frames.push(try!(read_frame(&mut reader)));
        }

        Ok(AnimGroup {
            palette: palette,
            frame_count: frame_count,
            frames: frames
        })
    }
}
<|endoftext|>