<commit_before>// (C)opyleft 2013-2015 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#![crate_name="bloomfilter"]
#![crate_type = "rlib"]
#![warn(non_camel_case_types, non_upper_case_globals, unused_qualifications)]
#![feature(core, hash, collections)]

extern crate collections;
extern crate rand;

use std::cmp;
use std::f64;
use std::hash::{Hash, Hasher, SipHasher};
use std::num::Float;
use collections::bitv;

#[cfg(test)]
use std::rand::Rng;

/// Bloom filter structure
pub struct Bloom {
    bitmap: bitv::Bitv,
    bitmap_bits: u64,
    k_num: u32,
    sips: [SipHasher; 2]
}

impl Bloom {
/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: usize, items_count: usize) -> Bloom {
        assert!(bitmap_size > 0 && items_count > 0);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::from_elem(bitmap_bits as usize, false);
        let sips = [ Bloom::sip_new(), Bloom::sip_new() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            sips: sips
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: usize, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: usize, fp_p: f64) -> usize {
        assert!(items_count > 0);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = f64::consts::LN_2;
        let log2_2 = log2 * log2;
        ((items_count as f64) * Float::ln(fp_p) / (-8.0 * log2_2)).ceil() as usize
    }

/// Record the presence of an item.
    pub fn set<T>(& mut self, item: T) where T: Hash<SipHasher> {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T>(&self, item: T) -> bool where T: Hash<SipHasher> {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            if self.bitmap.get(bit_offset).unwrap() == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T>(&mut self, item: T)
                               -> bool where T: Hash<SipHasher> {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            if self.bitmap.get(bit_offset).unwrap() == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

/// Return the number of bits in the filter
    pub fn number_of_bits(&self) -> u64 {
        self.bitmap_bits
    }

/// Return the number of hash functions used for `check` and `set`
    pub fn number_of_hash_functions(&self) -> u32 {
        self.k_num
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: usize) -> u32 {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * Float::ln(2.0f64)).ceil() as u32;
        cmp::max(k_num, 1)
    }

    fn bloom_hash<T>(&self, hashes: & mut [u64; 2],
                  item: &T, k_i: u32) -> u64 where T: Hash<SipHasher> {
        if k_i < 2 {
            let sip = &mut self.sips[k_i as usize].clone();
            item.hash(sip);
            let hash = sip.finish();
            hashes[k_i as usize] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }

    fn sip_new() -> SipHasher {
        let mut rng = rand::thread_rng();
        SipHasher::new_with_keys(rand::Rand::rand(& mut rng),
                                 rand::Rand::rand(& mut rng))
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::thread_rng().gen_iter::<u8>().take(16).collect();
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::thread_rng().gen_iter::<u8>().take(16).collect();
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<commit_msg>Use the rand crate to run the tests<commit_after>// (C)opyleft 2013-2015 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#![crate_name="bloomfilter"]
#![crate_type = "rlib"]
#![warn(non_camel_case_types, non_upper_case_globals, unused_qualifications)]
#![feature(core, hash, collections)]

extern crate collections;
extern crate rand;

use std::cmp;
use std::f64;
use std::hash::{Hash, Hasher, SipHasher};
use std::num::Float;
use collections::bitv;

#[cfg(test)]
use rand::Rng;

/// Bloom filter structure
pub struct Bloom {
    bitmap: bitv::Bitv,
    bitmap_bits: u64,
    k_num: u32,
    sips: [SipHasher; 2]
}

impl Bloom {
/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: usize, items_count: usize) -> Bloom {
        assert!(bitmap_size > 0 && items_count > 0);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::from_elem(bitmap_bits as usize, false);
        let sips = [ Bloom::sip_new(), Bloom::sip_new() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            sips: sips
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: usize, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: usize, fp_p: f64) -> usize {
        assert!(items_count > 0);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = f64::consts::LN_2;
        let log2_2 = log2 * log2;
        ((items_count as f64) * Float::ln(fp_p) / (-8.0 * log2_2)).ceil() as usize
    }

/// Record the presence of an item.
    pub fn set<T>(& mut self, item: T) where T: Hash<SipHasher> {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T>(&self, item: T) -> bool where T: Hash<SipHasher> {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            if self.bitmap.get(bit_offset).unwrap() == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T>(&mut self, item: T)
                               -> bool where T: Hash<SipHasher> {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            if self.bitmap.get(bit_offset).unwrap() == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

/// Return the number of bits in the filter
    pub fn number_of_bits(&self) -> u64 {
        self.bitmap_bits
    }

/// Return the number of hash functions used for `check` and `set`
    pub fn number_of_hash_functions(&self) -> u32 {
        self.k_num
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: usize) -> u32 {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * Float::ln(2.0f64)).ceil() as u32;
        cmp::max(k_num, 1)
    }

    fn bloom_hash<T>(&self, hashes: & mut [u64; 2],
                  item: &T, k_i: u32) -> u64 where T: Hash<SipHasher> {
        if k_i < 2 {
            let sip = &mut self.sips[k_i as usize].clone();
            item.hash(sip);
            let hash = sip.finish();
            hashes[k_i as usize] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }

    fn sip_new() -> SipHasher {
        let mut rng = rand::thread_rng();
        SipHasher::new_with_keys(rand::Rand::rand(& mut rng),
                                 rand::Rand::rand(& mut rng))
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::thread_rng().gen_iter::<u8>().take(16).collect();
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::thread_rng().gen_iter::<u8>().take(16).collect();
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<|endoftext|>


<commit_before>extern crate sorting;

/// Verify order of input vector as sorted by indices.
///
/// # Details
///
/// This function takes as arguments a vector of data and a vector representing the indices of the
/// first vector making it sorted.
///
/// The elements of the sorted vector are compared to the next ones to make sure they are less
/// or equal.
///
/// # Panics
///
/// The sorting of the data vector is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// let data: Vec<i32> = vec![4, 2, 3, 1];
/// let sorted_indices: Vec<usize> = vec![3, 1, 2, 0];
/// verify_sorting(&data, &sorted_indices);
/// ```
///
fn verify_sorting<T: PartialOrd>(array: &Vec<T>, sorted_indices: &Vec<usize>) {
    let n = array.len();
    assert_eq!(n , sorted_indices.len());

    if !array.is_empty() {
        for i in 0..n-1 {
            assert!(array[sorted_indices[i]] <= array[sorted_indices[i+1]]);
        }
    }
}

/// Call the specified sorting function on an empty vector of specific type.
///
/// # Details
///
/// This function takes as arguments a pointer to a sorting function acting and call
/// it on an empty vector of type T. The call should succeed and the index vector should
/// be empty as well.
///
/// # Panics
///
/// The emptiness of the sorted list is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
/// ```
///
fn test_empty_vec<T: PartialOrd>(sorting_fct: fn(&Vec<T>) -> Vec<usize>) {
    let to_sort: Vec<T> = vec![];
    let sorted_indices = sorting_fct(&to_sort);
    assert_eq!(sorted_indices, vec![]);
    assert!(sorted_indices.is_empty());
}

/// Call the specified sorting function on an specific vector of floats and validate the output.
///
/// # Details
///
/// This function takes as arguments:
///    1. An immutable vector of floats, to be sorted;
///    2. An immutable vector of indices as the reference to compare with;
///    3. A function pointer to be called on the first vector.
///
/// # Panics
///
/// This calls the function `verify_sorting()` which will assert if the result is not in order.
/// It also compares the resulting indices with expected values using asserts.
///
/// # Examples
///
/// ```
/// let to_sort: Vec<f32> = vec![6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
///                              3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];
///
/// let known_sorted_indices = vec![3, 15, 4, 2, 13, 8, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];
///
/// test_sort_vec_float::<f32>(&to_sort, &known_sorted_indices,
///                            sorting::simplesorts::insertion::sort);
/// ```
///
fn test_sort_vec_float<T: PartialOrd>(to_sort: &Vec<T>, expected_indices: &Vec<usize>,
                                      sorting_fct: fn(&Vec<T>) -> Vec<usize>) {
    let sorted_indices = sorting_fct(&to_sort);

    verify_sorting(&to_sort, &sorted_indices);

    assert_eq!(sorted_indices.len(), expected_indices.len());

    for i in 0..to_sort.len() {
        assert_eq!(sorted_indices[i], expected_indices[i]);
    }
}

/// Validate against empty vector (i8).
#[test]
fn simple_insertion_empty_vec_i8() {
    test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i16() {
    test_empty_vec::<i16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i32() {
    test_empty_vec::<i32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i64).
#[test]
fn simple_insertion_empty_vec_i64() {
    test_empty_vec::<i64>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (u8).
#[test]
fn simple_insertion_empty_vec_u8() {
    test_empty_vec::<u8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u16() {
    test_empty_vec::<u16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u32() {
    test_empty_vec::<u32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u64).
#[test]
fn simple_insertion_empty_vec_u64() {
    test_empty_vec::<u64>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (isize).
#[test]
fn simple_insertion_empty_vec_isize() {
    test_empty_vec::<isize>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (usize).
#[test]
fn simple_insertion_empty_vec_usize() {
    test_empty_vec::<usize>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (f32).
#[test]
fn simple_insertion_empty_vec_f32() {
    test_empty_vec::<f32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (f64).
#[test]
fn simple_insertion_empty_vec_f64() {
    test_empty_vec::<f64>(sorting::simplesorts::insertion::sort);
}

/// Validate sorting a vector of single precision values (i8).
#[test]
fn simple_insertion_vec_i8() {
    let to_sort: Vec<i8> = vec![6,  5,  3,  1,  2, 4, 10, 7,
                                3, 32, 44, 56, 67, 3, 44, 2];

    let known_sorted_indices = vec![3, 4, 15, 2, 8, 13, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<i8>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (i16).
#[test]
fn simple_insertion_vec_i16() {
    let to_sort: Vec<i16> = vec![6,  5,  3,  1,  2, 4, 10, 7,
                                 3, 32, 44, 56, 67, 3, 44, 2];

    let known_sorted_indices = vec![3, 4, 15, 2, 8, 13, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<i16>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (i32).
#[test]
fn simple_insertion_vec_i32() {
    let to_sort: Vec<i32> = vec![6,  5,  3,  1,  2, 4, 10, 7,
                                 3, 32, 44, 56, 67, 3, 44, 2];

    let known_sorted_indices = vec![3, 4, 15, 2, 8, 13, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<i32>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (i64).
#[test]
fn simple_insertion_vec_i64() {
    let to_sort: Vec<i64> = vec![6,  5,  3,  1,  2, 4, 10, 7,
                                 3, 32, 44, 56, 67, 3, 44, 2];

    let known_sorted_indices = vec![3, 4, 15, 2, 8, 13, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<i64>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}

/// Validate sorting a vector of single precision values (u8).
#[test]
fn simple_insertion_vec_u8() {
    let to_sort: Vec<u8> = vec![6,  5,  3,  1,  2, 4, 10, 7,
                                3, 32, 44, 56, 67, 3, 44, 2];

    let known_sorted_indices = vec![3, 4, 15, 2, 8, 13, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<u8>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (u16).
#[test]
fn simple_insertion_vec_u16() {
    let to_sort: Vec<u16> = vec![6,  5,  3,  1,  2, 4, 10, 7,
                                 3, 32, 44, 56, 67, 3, 44, 2];

    let known_sorted_indices = vec![3, 4, 15, 2, 8, 13, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<u16>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (u32).
#[test]
fn simple_insertion_vec_u32() {
    let to_sort: Vec<u32> = vec![6,  5,  3,  1,  2, 4, 10, 7,
                                 3, 32, 44, 56, 67, 3, 44, 2];

    let known_sorted_indices = vec![3, 4, 15, 2, 8, 13, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<u32>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (u64).
#[test]
fn simple_insertion_vec_u64() {
    let to_sort: Vec<u64> = vec![6,  5,  3,  1,  2, 4, 10, 7,
                                 3, 32, 44, 56, 67, 3, 44, 2];

    let known_sorted_indices = vec![3, 4, 15, 2, 8, 13, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<u64>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}

/// Validate sorting a vector of single precision values (f32).
#[test]
fn simple_insertion_vec_f32() {
    let to_sort: Vec<f32> = vec![6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
                                 3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];

    let known_sorted_indices = vec![3, 15, 4, 2, 13, 8, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<f32>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}

/// Validate sorting a vector of double precision values (f64).
#[test]
fn simple_insertion_vec_f64() {
    let to_sort: Vec<f64> = vec![6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
                                 3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];

    let known_sorted_indices = vec![3, 15, 4, 2, 13, 8, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

    test_sort_vec_float::<f64>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
<commit_msg>Move constant arrays to top.<commit_after>extern crate sorting;

const TO_SORT_I8:  [i8;  16] = [6,  5,  3,  1,  2, 4, 10, 7, 3, 32, 44, 56, 67, 3, 44, 2];
const TO_SORT_I16: [i16; 16] = [6,  5,  3,  1,  2, 4, 10, 7, 3, 32, 44, 56, 67, 3, 44, 2];
const TO_SORT_I32: [i32; 16] = [6,  5,  3,  1,  2, 4, 10, 7, 3, 32, 44, 56, 67, 3, 44, 2];
const TO_SORT_I64: [i64; 16] = [6,  5,  3,  1,  2, 4, 10, 7, 3, 32, 44, 56, 67, 3, 44, 2];
const TO_SORT_U8:  [u8;  16] = [6,  5,  3,  1,  2, 4, 10, 7, 3, 32, 44, 56, 67, 3, 44, 2];
const TO_SORT_U16: [u16; 16] = [6,  5,  3,  1,  2, 4, 10, 7, 3, 32, 44, 56, 67, 3, 44, 2];
const TO_SORT_U32: [u32; 16] = [6,  5,  3,  1,  2, 4, 10, 7, 3, 32, 44, 56, 67, 3, 44, 2];
const TO_SORT_U64: [u64; 16] = [6,  5,  3,  1,  2, 4, 10, 7, 3, 32, 44, 56, 67, 3, 44, 2];
const SORTED_IND_INT:  [usize; 16] = [3, 4, 15, 2, 8, 13, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

const TO_SORT_F32: [f32; 16] = [6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
                                3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];
const TO_SORT_F64: [f64; 16] = [6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
                                3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];
const SORTED_IND_FLOAT: [usize; 16] = [3, 15, 4, 2, 13, 8, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];

/// Verify order of input vector as sorted by indices.
///
/// # Details
///
/// This function takes as arguments a vector of data and a vector representing the indices of the
/// first vector making it sorted.
///
/// The elements of the sorted vector are compared to the next ones to make sure they are less
/// or equal.
///
/// # Panics
///
/// The sorting of the data vector is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// let data: Vec<i32> = vec![4, 2, 3, 1];
/// let sorted_indices: Vec<usize> = vec![3, 1, 2, 0];
/// verify_sorting(&data, &sorted_indices);
/// ```
///
fn verify_sorting<T: PartialOrd>(array: &Vec<T>, sorted_indices: &Vec<usize>) {
    let n = array.len();
    assert_eq!(n , sorted_indices.len());

    if !array.is_empty() {
        for i in 0..n-1 {
            assert!(array[sorted_indices[i]] <= array[sorted_indices[i+1]]);
        }
    }
}

/// Call the specified sorting function on an empty vector of specific type.
///
/// # Details
///
/// This function takes as arguments a pointer to a sorting function acting and call
/// it on an empty vector of type T. The call should succeed and the index vector should
/// be empty as well.
///
/// # Panics
///
/// The emptiness of the sorted list is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
/// ```
///
fn test_empty_vec<T: PartialOrd>(sorting_fct: fn(&Vec<T>) -> Vec<usize>) {
    let to_sort: Vec<T> = vec![];
    let sorted_indices = sorting_fct(&to_sort);
    assert_eq!(sorted_indices, vec![]);
    assert!(sorted_indices.is_empty());
}

/// Call the specified sorting function on an specific vector of floats and validate the output.
///
/// # Details
///
/// This function takes as arguments:
///    1. An immutable vector of floats, to be sorted;
///    2. An immutable vector of indices as the reference to compare with;
///    3. A function pointer to be called on the first vector.
///
/// # Panics
///
/// This calls the function `verify_sorting()` which will assert if the result is not in order.
/// It also compares the resulting indices with expected values using asserts.
///
/// # Examples
///
/// ```
/// let to_sort: Vec<f32> = vec![6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
///                              3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];
///
/// let known_sorted_indices = vec![3, 15, 4, 2, 13, 8, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12];
///
/// test_sort_vec_float::<f32>(&to_sort, &known_sorted_indices,
///                            sorting::simplesorts::insertion::sort);
/// ```
///
fn test_sort_vec_float<T: PartialOrd>(to_sort: &Vec<T>, expected_indices: &Vec<usize>,
                                      sorting_fct: fn(&Vec<T>) -> Vec<usize>) {
    let sorted_indices = sorting_fct(&to_sort);

    verify_sorting(&to_sort, &sorted_indices);

    assert_eq!(sorted_indices.len(), expected_indices.len());

    for i in 0..to_sort.len() {
        assert_eq!(sorted_indices[i], expected_indices[i]);
    }
}

/// Validate against empty vector (i8).
#[test]
fn simple_insertion_empty_vec_i8() {
    test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i16() {
    test_empty_vec::<i16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i32() {
    test_empty_vec::<i32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i64).
#[test]
fn simple_insertion_empty_vec_i64() {
    test_empty_vec::<i64>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (u8).
#[test]
fn simple_insertion_empty_vec_u8() {
    test_empty_vec::<u8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u16() {
    test_empty_vec::<u16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u32() {
    test_empty_vec::<u32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u64).
#[test]
fn simple_insertion_empty_vec_u64() {
    test_empty_vec::<u64>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (isize).
#[test]
fn simple_insertion_empty_vec_isize() {
    test_empty_vec::<isize>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (usize).
#[test]
fn simple_insertion_empty_vec_usize() {
    test_empty_vec::<usize>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (f32).
#[test]
fn simple_insertion_empty_vec_f32() {
    test_empty_vec::<f32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (f64).
#[test]
fn simple_insertion_empty_vec_f64() {
    test_empty_vec::<f64>(sorting::simplesorts::insertion::sort);
}

/// Validate sorting a vector of single precision values (i8).
#[test]
fn simple_insertion_vec_i8() {
    let to_sort: Vec<_> = From::from(&TO_SORT_I8[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_INT[..]);

    test_sort_vec_float::<i8>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (i16).
#[test]
fn simple_insertion_vec_i16() {
    let to_sort: Vec<_> = From::from(&TO_SORT_I16[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_INT[..]);

    test_sort_vec_float::<i16>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (i32).
#[test]
fn simple_insertion_vec_i32() {
    let to_sort: Vec<_> = From::from(&TO_SORT_I32[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_INT[..]);

    test_sort_vec_float::<i32>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (i64).
#[test]
fn simple_insertion_vec_i64() {
    let to_sort: Vec<_> = From::from(&TO_SORT_I64[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_INT[..]);

    test_sort_vec_float::<i64>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}

/// Validate sorting a vector of single precision values (u8).
#[test]
fn simple_insertion_vec_u8() {
    let to_sort: Vec<_> = From::from(&TO_SORT_U8[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_INT[..]);

    test_sort_vec_float::<u8>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (u16).
#[test]
fn simple_insertion_vec_u16() {
    let to_sort: Vec<_> = From::from(&TO_SORT_U16[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_INT[..]);

    test_sort_vec_float::<u16>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (u32).
#[test]
fn simple_insertion_vec_u32() {
    let to_sort: Vec<_> = From::from(&TO_SORT_U32[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_INT[..]);

    test_sort_vec_float::<u32>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
/// Validate sorting a vector of single precision values (u64).
#[test]
fn simple_insertion_vec_u64() {
    let to_sort: Vec<_> = From::from(&TO_SORT_U64[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_INT[..]);

    test_sort_vec_float::<u64>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}

/// Validate sorting a vector of single precision values (f32).
#[test]
fn simple_insertion_vec_f32() {
    let to_sort: Vec<_> = From::from(&TO_SORT_F32[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_FLOAT[..]);

    test_sort_vec_float::<f32>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}

/// Validate sorting a vector of double precision values (f64).
#[test]
fn simple_insertion_vec_f64() {
    let to_sort: Vec<_> = From::from(&TO_SORT_F64[..]);
    let known_sorted_indices: Vec<usize> = From::from(&SORTED_IND_FLOAT[..]);

    test_sort_vec_float::<f64>(&to_sort, &known_sorted_indices,
                               sorting::simplesorts::insertion::sort);
}
<|endoftext|>


<commit_before>#![allow(unused_variables, unused_imports, dead_code)]

extern crate nom;
extern crate m3u8_rs;

use std::fs;
use std::path;
use m3u8_rs::*;
use m3u8_rs::playlist::*;
use std::io::Read;
use std::fs::File;
use nom::*;
use std::collections::HashMap;

fn all_sample_m3u_playlists() -> Vec<path::PathBuf> {
    let path: std::path::PathBuf = ["sample-playlists"].iter().collect();
    fs::read_dir(path.to_str().unwrap()).unwrap()
        .filter_map(Result::ok)
        .map(|dir| dir.path())
        .filter(|path| path.extension().map_or(false, |ext| ext == "m3u8"))
        .collect()
}

fn getm3u(path: &str) -> String {
    let mut buf = String::new();
    let mut file = fs::File::open(path).expect(&format!("Can't find m3u8: {}", path));
    let u = file.read_to_string(&mut buf).expect("Can't read file");
    buf
}

fn get_sample_playlist(name: &str) -> String {
    let path: std::path::PathBuf = ["sample-playlists", name].iter().collect();
    getm3u(path.to_str().unwrap())
}

// -----------------------------------------------------------------------------------------------
// Playlist

fn print_parse_playlist_test(playlist_name: &str) -> bool {
    let input: String = get_sample_playlist(playlist_name);
    println!("Parsing playlist file: {:?}", playlist_name);
    let parsed = parse_playlist(input.as_bytes());

    if let IResult::Done(i,o) = parsed {
        println!("{:?}", o);
        true
    }
    else {
        println!("Parsing failed:\n {:?}", parsed);
        false
    }
}

#[test]
fn playlist_master_with_alternatives() {
    assert!(print_parse_playlist_test("master-with-alternatives.m3u8"));
}

#[test]
fn playlist_master_with_i_frame_stream_inf() {
    assert!(print_parse_playlist_test("master-with-i-frame-stream-inf.m3u8"));
}

#[test]
fn playlist_master_with_multiple_codecs() {
    assert!(print_parse_playlist_test("master-with-multiple-codecs.m3u8"));
}

// -- Media playlists

#[test]
fn playlist_media_standard() {
    assert!(print_parse_playlist_test("mediaplaylist.m3u8"));
}

#[test]
fn playlist_media_without_segments() {
    assert!(print_parse_playlist_test("media-playlist-without-segments.m3u8"));
}

// -----------------------------------------------------------------------------------------------
// Playlist with no newline end

#[test]
fn playlist_not_ending_in_newline_master() {
    assert!(print_parse_playlist_test("master-not-ending-in-newline.m3u8"));
}

#[test]
fn playlist_not_ending_in_newline_media() {
    assert!(print_parse_playlist_test("media-not-ending-in-newline.m3u8"));
}

// -----------------------------------------------------------------------------------------------
// Playlist type detection tests

#[test]
fn playlist_type_is_master() {
    let input = get_sample_playlist("master.m3u8");
    let result = is_master_playlist(input.as_bytes());
    assert_eq!(true, result);
}

// #[test]
// fn playlist_type_with_unkown_tag() {
//     let input = get_sample_playlist("!!");
//     let result = is_master_playlist(input.as_bytes());
//     println!("Playlist_type_with_unkown_tag is master playlist: {:?}", result);
//     assert_eq!(true, result);
// }

#[test]
fn playlist_types() {
    for path_buf in all_sample_m3u_playlists() {
        let path = path_buf.to_str().unwrap();
        let input = getm3u(path);
        let is_master = is_master_playlist(input.as_bytes());

        assert!(path.to_lowercase().contains("master") == is_master);

        println!("{:?} = {:?}", path, is_master);
    }
}

// -----------------------------------------------------------------------------------------------
// Variant

#[test]
fn variant_stream() {
    let input = b"#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"xxx\"\n";
    let result = variant_stream_tag(input);
    println!("{:?}", result);
}

// -----------------------------------------------------------------------------------------------
// Other

#[test]
fn test_key_value_pairs_trailing_equals() {
    let res = key_value_pairs(b"BANDWIDTH=395000,CODECS=\"avc1.4d001f,mp4a.40.2\"\r\nrest=");
    println!("{:?}\n\n", res);
}

#[test]
fn test_key_value_pairs_multiple_quoted_values() {
    assert_eq!(
        key_value_pairs(b"BANDWIDTH=86000,URI=\"low/iframe.m3u8\",PROGRAM-ID=1,RESOLUTION=\"1x1\",VIDEO=1\nrest"),
        IResult::Done(
            "\nrest".as_bytes(),
            vec![
                ("BANDWIDTH".to_string(), "86000".to_string()),
                ("URI".to_string(), "low/iframe.m3u8".to_string()),
                ("PROGRAM-ID".to_string(), "1".to_string()),
                ("RESOLUTION".to_string(), "1x1".to_string()),
                ("VIDEO".to_string(), "1".to_string())
            ].into_iter().collect::<HashMap<String,String>>()
        )
    );
}

#[test]
fn test_key_value_pairs_quotes() {
    let res = key_value_pairs(b"BANDWIDTH=300000,CODECS=\"avc1.42c015,mp4a.40.2\"\r\nrest");
    println!("{:?}\n\n", res);
}

#[test]
fn test_key_value_pairs() {
    let res = key_value_pairs(b"BANDWIDTH=300000,RESOLUTION=22x22,VIDEO=1\r\nrest=");
    println!("{:?}\n\n", res);
}

#[test]
fn test_key_value_pair() {
    assert_eq!(
        key_value_pair(b"PROGRAM-ID=1,rest"),
        IResult::Done(
            "rest".as_bytes(),
            ("PROGRAM-ID".to_string(), "1".to_string())
        )
    );
}

#[test]
fn comment() {
    assert_eq!(
        comment_tag(b"#Hello\nxxx"),
        IResult::Done("xxx".as_bytes(), "Hello".to_string())
    );
}

#[test]
fn quotes() {
    assert_eq!(
        quoted(b"\"value\"rest"),
        IResult::Done("rest".as_bytes(), "value".to_string())
    );
}

#[test]
fn consume_empty_line() {
    let line = consume_line(b"\r\nrest");
    println!("{:?}", line);
}

#[test]
fn float_() {
    assert_eq!(
        float(b"33.22rest"),
        IResult::Done("rest".as_bytes(), 33.22f32)
    );
}

#[test]
fn float_no_decimal() {
    assert_eq!(
        float(b"33rest"),
        IResult::Done("rest".as_bytes(), 33f32)
    );
}

#[test]
fn float_should_ignore_trailing_dot() {
    assert_eq!(
        float(b"33.rest"),
        IResult::Done(".rest".as_bytes(), 33f32)
    );
}

#[test]
fn parse_duration_title() {
    assert_eq!(
        duration_title_tag(b"2.002,title\nrest"),
        IResult::Done("rest".as_bytes(), (2.002f32, Some("title".to_string())))
    );
}

// -----------------------------------------------------------------------------------------------
// Creating playlists

fn print_create_and_parse_playlist(playlist_original: &mut Playlist) -> Playlist {
    let mut utf8: Vec<u8> = Vec::new();
    playlist_original.write_to(&mut utf8).unwrap();

    let m3u8_str: &str = std::str::from_utf8(&utf8).unwrap();

    let playlist_parsed = match *playlist_original {
        Playlist::MasterPlaylist(_) =>
            Playlist::MasterPlaylist(parse_master_playlist_res(m3u8_str.as_bytes()).unwrap()),
        Playlist::MediaPlaylist(_) =>
            Playlist::MediaPlaylist(parse_media_playlist_res(m3u8_str.as_bytes()).unwrap()),
    };

    print!("\n\n---- utf8 result\n\n{}", m3u8_str);
    print!("\n---- Original\n\n{:?}", playlist_original);
    print!("\n\n---- Parsed\n\n{:?}\n\n", playlist_parsed);

    playlist_parsed
}

#[test]
fn create_and_parse_master_playlist_empty() {
    let mut playlist_original = Playlist::MasterPlaylist(MasterPlaylist { ..Default::default() });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}

#[test]
fn create_and_parse_master_playlist_full() {

    let mut playlist_original = Playlist::MasterPlaylist(MasterPlaylist {
        version: 6,
        variants: vec![
            VariantStream {
                is_i_frame: false,
                uri: "masterplaylist-uri".into(),
                bandwidth: "10010010".into(),
                average_bandwidth: Some("10010010".into()),
                codecs: "TheCODEC".into(),
                resolution: Some("1000x3000".into()),
                frame_rate: Some("60".into()),
                audio: Some("audio".into()),
                video: Some("video".into()),
                subtitles: Some("subtitles".into()),
                closed_captions: Some("closed_captions".into()),
                alternatives: vec! [
                    AlternativeMedia {
                        media_type: AlternativeMediaType::Audio,
                        uri: Some("alt-media-uri".into()),
                        group_id: "group-id".into(),
                        language: Some("language".into()),
                        assoc_language: Some("assoc-language".into()),
                        name: "Xmedia".into(),
                        default: true, // Its absence indicates an implicit value of NO
                        autoselect: true, // Its absence indicates an implicit value of NO
                        forced: true, // Its absence indicates an implicit value of NO
                        instream_id: Some("instream_id".into()),
                        characteristics: Some("characteristics".into()),
                    }
                ]
            }
        ],
        session_data: Some(SessionData {
            data_id: "****".into(),
            value: "%%%%".into(),
            uri: "++++".into(),
            language: Some("SessionDataLanguage".into()),
        }),
        session_key: Some(SessionKey(Key {
            method: "AES-128".into(),
            uri: Some("https://secure.domain.com".into()),
            iv: Some("0xb059217aa2649ce170b734".into()),
            keyformat: Some("xXkeyformatXx".into()),
            keyformatversions: Some("xXFormatVers".into()),
        })),
        start: Some(Start {
            time_offset: "123123123".into(),
            precise: Some("YES".into()),
        }),
        independent_segments: true,
    });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}

#[test]
fn create_and_parse_media_playlist_empty() {
    let mut playlist_original = Playlist::MediaPlaylist(MediaPlaylist { ..Default::default() });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}

#[test]
fn create_and_parse_media_playlist_single_segment() {
    let mut playlist_original = Playlist::MediaPlaylist(MediaPlaylist {
        segments: vec![
            MediaSegment {
                uri: "20140311T113819-01-338559live.ts".into(),
                duration: 2.002,
                title: Some("hey".into()),
                ..Default::default()
            },
        ],
         ..Default::default()
    });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}

#[test]
fn create_and_parse_media_playlist_full() {

    let mut playlist_original = Playlist::MediaPlaylist(MediaPlaylist {
        version: 4,
        target_duration: 3.0,
        media_sequence: 338559,
        discontinuity_sequence: 1234,
        end_list: true,
        playlist_type: Some(MediaPlaylistType::Vod),
        i_frames_only: true,
        start: Some(Start {
            time_offset: "9999".into(),
            precise: Some("YES".into()),
        }),
        independent_segments: true,
        segments: vec![
            MediaSegment {
                uri: "20140311T113819-01-338559live.ts".into(),
                duration: 2.002,
                title: Some("338559".into()),
                byte_range: Some(ByteRange::from("137116@497036")),
                discontinuity: true,
                key: Some(Key {
                    method: "AES-128".into(),
                    uri: Some("https://secure.domain.com".into()),
                    iv: Some("0xb059217aa2649ce170b734".into()),
                    keyformat: Some("xXkeyformatXx".into()),
                    keyformatversions: Some("xXFormatVers".into()),
                }),
                map: Some(Map {
                    uri: "www.map-uri.com".into(),
                    byte_range: Some(ByteRange::from("137116@497036")),
                }),
                program_date_time: Some("broodlordinfestorgg".into()),
                daterange: None,
            },
        ],
    });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}
<commit_msg>Added quick parse roundtrip test<commit_after>#![allow(unused_variables, unused_imports, dead_code)]

extern crate nom;
extern crate m3u8_rs;

use std::fs;
use std::path;
use m3u8_rs::*;
use m3u8_rs::playlist::*;
use std::io::Read;
use std::fs::File;
use nom::*;
use std::collections::HashMap;

fn all_sample_m3u_playlists() -> Vec<path::PathBuf> {
    let path: std::path::PathBuf = ["sample-playlists"].iter().collect();
    fs::read_dir(path.to_str().unwrap()).unwrap()
        .filter_map(Result::ok)
        .map(|dir| dir.path())
        .filter(|path| path.extension().map_or(false, |ext| ext == "m3u8"))
        .collect()
}

fn getm3u(path: &str) -> String {
    let mut buf = String::new();
    let mut file = fs::File::open(path).expect(&format!("Can't find m3u8: {}", path));
    let u = file.read_to_string(&mut buf).expect("Can't read file");
    buf
}

fn get_sample_playlist(name: &str) -> String {
    let path: std::path::PathBuf = ["sample-playlists", name].iter().collect();
    getm3u(path.to_str().unwrap())
}

// -----------------------------------------------------------------------------------------------
// Playlist

fn print_parse_playlist_test(playlist_name: &str) -> bool {
    let input: String = get_sample_playlist(playlist_name);
    println!("Parsing playlist file: {:?}", playlist_name);
    let parsed = parse_playlist(input.as_bytes());

    if let IResult::Done(i,o) = parsed {
        println!("{:?}", o);
        true
    }
    else {
        println!("Parsing failed:\n {:?}", parsed);
        false
    }
}

#[test]
fn playlist_master_with_alternatives() {
    assert!(print_parse_playlist_test("master-with-alternatives.m3u8"));
}

#[test]
fn playlist_master_with_i_frame_stream_inf() {
    assert!(print_parse_playlist_test("master-with-i-frame-stream-inf.m3u8"));
}

#[test]
fn playlist_master_with_multiple_codecs() {
    assert!(print_parse_playlist_test("master-with-multiple-codecs.m3u8"));
}

// -- Media playlists

#[test]
fn playlist_media_standard() {
    assert!(print_parse_playlist_test("mediaplaylist.m3u8"));
}

#[test]
fn playlist_media_without_segments() {
    assert!(print_parse_playlist_test("media-playlist-without-segments.m3u8"));
}

// -----------------------------------------------------------------------------------------------
// Playlist with no newline end

#[test]
fn playlist_not_ending_in_newline_master() {
    assert!(print_parse_playlist_test("master-not-ending-in-newline.m3u8"));
}

#[test]
fn playlist_not_ending_in_newline_media() {
    assert!(print_parse_playlist_test("media-not-ending-in-newline.m3u8"));
}

// -----------------------------------------------------------------------------------------------
// Playlist type detection tests

#[test]
fn playlist_type_is_master() {
    let input = get_sample_playlist("master.m3u8");
    let result = is_master_playlist(input.as_bytes());
    assert_eq!(true, result);
}

// #[test]
// fn playlist_type_with_unkown_tag() {
//     let input = get_sample_playlist("!!");
//     let result = is_master_playlist(input.as_bytes());
//     println!("Playlist_type_with_unkown_tag is master playlist: {:?}", result);
//     assert_eq!(true, result);
// }

#[test]
fn playlist_types() {
    for path_buf in all_sample_m3u_playlists() {
        let path = path_buf.to_str().unwrap();
        let input = getm3u(path);
        let is_master = is_master_playlist(input.as_bytes());

        assert!(path.to_lowercase().contains("master") == is_master);

        println!("{:?} = {:?}", path, is_master);
    }
}

// -----------------------------------------------------------------------------------------------
// Variant

#[test]
fn variant_stream() {
    let input = b"#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"xxx\"\n";
    let result = variant_stream_tag(input);
    println!("{:?}", result);
}

// -----------------------------------------------------------------------------------------------
// Other

#[test]
fn test_key_value_pairs_trailing_equals() {
    let res = key_value_pairs(b"BANDWIDTH=395000,CODECS=\"avc1.4d001f,mp4a.40.2\"\r\nrest=");
    println!("{:?}\n\n", res);
}

#[test]
fn test_key_value_pairs_multiple_quoted_values() {
    assert_eq!(
        key_value_pairs(b"BANDWIDTH=86000,URI=\"low/iframe.m3u8\",PROGRAM-ID=1,RESOLUTION=\"1x1\",VIDEO=1\nrest"),
        IResult::Done(
            "\nrest".as_bytes(),
            vec![
                ("BANDWIDTH".to_string(), "86000".to_string()),
                ("URI".to_string(), "low/iframe.m3u8".to_string()),
                ("PROGRAM-ID".to_string(), "1".to_string()),
                ("RESOLUTION".to_string(), "1x1".to_string()),
                ("VIDEO".to_string(), "1".to_string())
            ].into_iter().collect::<HashMap<String,String>>()
        )
    );
}

#[test]
fn test_key_value_pairs_quotes() {
    let res = key_value_pairs(b"BANDWIDTH=300000,CODECS=\"avc1.42c015,mp4a.40.2\"\r\nrest");
    println!("{:?}\n\n", res);
}

#[test]
fn test_key_value_pairs() {
    let res = key_value_pairs(b"BANDWIDTH=300000,RESOLUTION=22x22,VIDEO=1\r\nrest=");
    println!("{:?}\n\n", res);
}

#[test]
fn test_key_value_pair() {
    assert_eq!(
        key_value_pair(b"PROGRAM-ID=1,rest"),
        IResult::Done(
            "rest".as_bytes(),
            ("PROGRAM-ID".to_string(), "1".to_string())
        )
    );
}

#[test]
fn comment() {
    assert_eq!(
        comment_tag(b"#Hello\nxxx"),
        IResult::Done("xxx".as_bytes(), "Hello".to_string())
    );
}

#[test]
fn quotes() {
    assert_eq!(
        quoted(b"\"value\"rest"),
        IResult::Done("rest".as_bytes(), "value".to_string())
    );
}

#[test]
fn consume_empty_line() {
    let line = consume_line(b"\r\nrest");
    println!("{:?}", line);
}

#[test]
fn float_() {
    assert_eq!(
        float(b"33.22rest"),
        IResult::Done("rest".as_bytes(), 33.22f32)
    );
}

#[test]
fn float_no_decimal() {
    assert_eq!(
        float(b"33rest"),
        IResult::Done("rest".as_bytes(), 33f32)
    );
}

#[test]
fn float_should_ignore_trailing_dot() {
    assert_eq!(
        float(b"33.rest"),
        IResult::Done(".rest".as_bytes(), 33f32)
    );
}

#[test]
fn parse_duration_title() {
    assert_eq!(
        duration_title_tag(b"2.002,title\nrest"),
        IResult::Done("rest".as_bytes(), (2.002f32, Some("title".to_string())))
    );
}

// -----------------------------------------------------------------------------------------------
// Creating playlists

fn print_create_and_parse_playlist(playlist_original: &mut Playlist) -> Playlist {
    let mut utf8: Vec<u8> = Vec::new();
    playlist_original.write_to(&mut utf8).unwrap();

    let m3u8_str: &str = std::str::from_utf8(&utf8).unwrap();

    let playlist_parsed = match *playlist_original {
        Playlist::MasterPlaylist(_) =>
            Playlist::MasterPlaylist(parse_master_playlist_res(m3u8_str.as_bytes()).unwrap()),
        Playlist::MediaPlaylist(_) =>
            Playlist::MediaPlaylist(parse_media_playlist_res(m3u8_str.as_bytes()).unwrap()),
    };

    print!("\n\n---- utf8 result\n\n{}", m3u8_str);
    print!("\n---- Original\n\n{:?}", playlist_original);
    print!("\n\n---- Parsed\n\n{:?}\n\n", playlist_parsed);

    playlist_parsed
}

#[test]
fn create_and_parse_master_playlist_empty() {
    let mut playlist_original = Playlist::MasterPlaylist(MasterPlaylist { ..Default::default() });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}

#[test]
fn create_and_parse_master_playlist_full() {

    let mut playlist_original = Playlist::MasterPlaylist(MasterPlaylist {
        version: 6,
        variants: vec![
            VariantStream {
                is_i_frame: false,
                uri: "masterplaylist-uri".into(),
                bandwidth: "10010010".into(),
                average_bandwidth: Some("10010010".into()),
                codecs: "TheCODEC".into(),
                resolution: Some("1000x3000".into()),
                frame_rate: Some("60".into()),
                audio: Some("audio".into()),
                video: Some("video".into()),
                subtitles: Some("subtitles".into()),
                closed_captions: Some("closed_captions".into()),
                alternatives: vec! [
                    AlternativeMedia {
                        media_type: AlternativeMediaType::Audio,
                        uri: Some("alt-media-uri".into()),
                        group_id: "group-id".into(),
                        language: Some("language".into()),
                        assoc_language: Some("assoc-language".into()),
                        name: "Xmedia".into(),
                        default: true, // Its absence indicates an implicit value of NO
                        autoselect: true, // Its absence indicates an implicit value of NO
                        forced: true, // Its absence indicates an implicit value of NO
                        instream_id: Some("instream_id".into()),
                        characteristics: Some("characteristics".into()),
                    }
                ]
            }
        ],
        session_data: Some(SessionData {
            data_id: "****".into(),
            value: "%%%%".into(),
            uri: "++++".into(),
            language: Some("SessionDataLanguage".into()),
        }),
        session_key: Some(SessionKey(Key {
            method: "AES-128".into(),
            uri: Some("https://secure.domain.com".into()),
            iv: Some("0xb059217aa2649ce170b734".into()),
            keyformat: Some("xXkeyformatXx".into()),
            keyformatversions: Some("xXFormatVers".into()),
        })),
        start: Some(Start {
            time_offset: "123123123".into(),
            precise: Some("YES".into()),
        }),
        independent_segments: true,
    });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}

#[test]
fn create_and_parse_media_playlist_empty() {
    let mut playlist_original = Playlist::MediaPlaylist(MediaPlaylist { ..Default::default() });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}

#[test]
fn create_and_parse_media_playlist_single_segment() {
    let mut playlist_original = Playlist::MediaPlaylist(MediaPlaylist {
        segments: vec![
            MediaSegment {
                uri: "20140311T113819-01-338559live.ts".into(),
                duration: 2.002,
                title: Some("hey".into()),
                ..Default::default()
            },
        ],
         ..Default::default()
    });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}

#[test]
fn create_and_parse_media_playlist_full() {

    let mut playlist_original = Playlist::MediaPlaylist(MediaPlaylist {
        version: 4,
        target_duration: 3.0,
        media_sequence: 338559,
        discontinuity_sequence: 1234,
        end_list: true,
        playlist_type: Some(MediaPlaylistType::Vod),
        i_frames_only: true,
        start: Some(Start {
            time_offset: "9999".into(),
            precise: Some("YES".into()),
        }),
        independent_segments: true,
        segments: vec![
            MediaSegment {
                uri: "20140311T113819-01-338559live.ts".into(),
                duration: 2.002,
                title: Some("338559".into()),
                byte_range: Some(ByteRange::from("137116@497036")),
                discontinuity: true,
                key: Some(Key {
                    method: "AES-128".into(),
                    uri: Some("https://secure.domain.com".into()),
                    iv: Some("0xb059217aa2649ce170b734".into()),
                    keyformat: Some("xXkeyformatXx".into()),
                    keyformatversions: Some("xXFormatVers".into()),
                }),
                map: Some(Map {
                    uri: "www.map-uri.com".into(),
                    byte_range: Some(ByteRange::from("137116@497036")),
                }),
                program_date_time: Some("broodlordinfestorgg".into()),
                daterange: None,
            },
        ],
    });
    let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
    assert_eq!(playlist_original, playlist_parsed);
}

//
// Roundtrip

#[test]
fn parsing_write_to_should_produce_the_same_structure() {
    for playlist in all_sample_m3u_playlists() {
        let input = getm3u(playlist.to_str().unwrap());

        let expected = parse_playlist_res(input.as_bytes()).unwrap();
        let mut written: Vec<u8> = Vec::new();
        expected.write_to(&mut written).unwrap();

        let actual = parse_playlist_res(&written).unwrap();

        assert_eq!(
            expected, actual,
            "\n\nFailed parser input:\n\n{}\n\nOriginal input:\n\n{}",
            std::str::from_utf8(&written).unwrap(), input);
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::process::Command;

#[derive(Debug)]
pub struct NiceOutput {
    pub status: i32,
    pub stdout: String,
    pub stderr: String,
}

fn run(args: Vec<&str>) -> NiceOutput {
    let mut directory = env::current_exe().unwrap();
            directory.pop(); // chop off exe name but leave "debug"
    let name = format!("to{}", env::consts::EXE_SUFFIX);
    let binary = directory.join(&name);

    let mut command = Command::new(binary);
            command.args(&args);
    let output = command.output().unwrap();
    let stdout = trim(output.stdout);
    let stderr = trim(output.stderr);

    return NiceOutput {
        status: output.status.code().unwrap(),
        stdout: stdout,
        stderr: stderr,
    };
}

fn trim(output: Vec<u8>) -> String {
    let string = String::from_utf8(output).unwrap();
    return String::from(string.trim());
}

#[test]
fn smoke_test() {
    let result = run(vec!["-h"]);
    assert_eq!(result.status, 0);
}

#[test]
fn version_test_long() {
    let result = run(vec!["--version"]);
    let version = env!("CARGO_PKG_VERSION");
    let expected = format!("to {}", version);

    assert_eq!(result.stdout, expected);
}

#[test]
fn version_test_short() {
    let result = run(vec!["-V"]);
    let version = env!("CARGO_PKG_VERSION");
    let expected = format!("to {}", version);

    assert_eq!(result.stdout, expected);
}
<commit_msg>Adds short/long test for help flags.<commit_after>use std::env;
use std::process::Command;

#[derive(Debug)]
pub struct NiceOutput {
    pub status: i32,
    pub stdout: String,
    pub stderr: String,
}

fn run(args: Vec<&str>) -> NiceOutput {
    let mut directory = env::current_exe().unwrap();
            directory.pop(); // chop off exe name but leave "debug"
    let name = format!("to{}", env::consts::EXE_SUFFIX);
    let binary = directory.join(&name);

    let mut command = Command::new(binary);
            command.args(&args);
    let output = command.output().unwrap();
    let stdout = trim(output.stdout);
    let stderr = trim(output.stderr);

    return NiceOutput {
        status: output.status.code().unwrap(),
        stdout: stdout,
        stderr: stderr,
    };
}

fn trim(output: Vec<u8>) -> String {
    let string = String::from_utf8(output).unwrap();
    return String::from(string.trim());
}

#[test]
fn help_long() {
    let result = run(vec!["--help"]);
    assert_eq!(result.status, 0);
}

#[test]
fn help_short() {
    let result = run(vec!["-h"]);
    assert_eq!(result.status, 0);
}

#[test]
fn version_long() {
    let result = run(vec!["--version"]);
    let version = env!("CARGO_PKG_VERSION");
    let expected = format!("to {}", version);

    assert_eq!(result.stdout, expected);
}

#[test]
fn version_short() {
    let result = run(vec!["-V"]);
    let version = env!("CARGO_PKG_VERSION");
    let expected = format!("to {}", version);

    assert_eq!(result.stdout, expected);
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Arithmetic/Rational
extern crate num;

use std::fmt;
use num::traits::{Zero, One};
use std::cmp::Ordering;
use std::ops::{Add, Mul, Neg, Sub, Div};

fn main() {
    for p in perfect_numbers(1 << 19) {
        println!("{} is perfect", p);
    }
}

fn perfect_numbers(max: i64) -> Vec<i64> {
    let mut ret=Vec::new();
    for candidate in 2..max {
        let mut sum=Frac::secure_new(1, candidate).unwrap();
        let max2=((candidate as f64).sqrt().floor()) as i64;

        for factor in 2..max2 + 1 {
            if candidate % factor == 0 {
                sum = sum + Frac::new(1, factor) + Frac::new(factor, candidate);
            }
        }
        if sum == Frac::new(1,1) {ret.push(candidate);}
    }
    ret
}
#[derive(Copy, Clone)]
struct Frac {
    num: i64,
    den: i64
}

fn gcd(m: i64, n:i64) -> i64 {
    let mut t: i64;
    let (mut m, mut n)=(m.abs(), n.abs());
    while n>0 { t = n; n = m % n; m = t; }
    m
}

fn lcm(m: i64, n:i64) -> i64 {
    m.abs() / gcd(m, n) * n.abs()
}

impl Frac {
    // fails on den=0
    fn new(num: i64, den:i64) -> Frac {
        let (n, d) = match (num, den) {
            (0, _)          => (0, 0),
            (n, d) if d<0   => (-n, -d),
            a @ _           => a
        };

        Frac {num: n, den: d}
    }

    // does not fail (returns Err on den=0)
    fn secure_new(num: i64, den:i64) -> Result<Frac, String> {
        if den==0 {
            Err("Error: Division by zero".to_string())
        } else {
            Ok(Frac::new(num, den))
        }
    }

    // fails on den=0, returns frac already in its reduced form
    fn new_reduced(num: i64, den:i64) -> Frac {
        Frac::new(num, den).reduce()
    }

    // reduces the fraction to lowest terms
    fn reduce(mut self) -> Frac {
        match self {
            z @ Frac{num:0, den:0} => z,
            _         => {
                let gcd=gcd(self.num, self.den);
                self.num /= gcd;
                self.den /= gcd;
                self
            }
        }
    }
}

impl fmt::Debug for Frac {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.num, self.den) {
            (_,1) | (0,0) => write!(f, "{}", self.num),
            (_,_) => write!(f, "{}/{}", self.num, self.den)
        }
    }
}

impl PartialEq for Frac {
    fn eq(&self, other: &Frac) -> bool {
        let (red_a, red_b) = (self.reduce(), other.reduce());
        red_a.num==red_b.num && red_a.den==red_b.den
    }
}

impl Eq for Frac {}

impl PartialOrd for Frac {
    fn partial_cmp(&self, other: &Frac) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Frac {
    fn cmp(&self, other: &Frac) -> Ordering {
        (self.num * other.den).cmp(&(self.den * other.num))
    }
}

impl Neg for Frac {
    type Output = Frac;

    fn neg(self) -> Frac {
        Frac{num:-self.num, den:self.den}
    }
}

impl Add for Frac {
    type Output = Frac;
    fn add(self, other: Frac) -> Frac {
        let (a, b)=(self.reduce(), other.reduce());
        let m = lcm(a.den, b.den);

        let na = a.num * m / a.den;
        let nb = b.num * m / b.den;
        Frac::new_reduced(na + nb, m)
    }
}

impl Sub for Frac {
    type Output = Frac;
    fn sub(self, other: Frac) -> Frac {
        self + (- other)
    }
}

impl Mul for Frac {
    type Output = Frac;
    fn mul(self, other: Frac) -> Frac {
        Frac::new_reduced(self.num * other.num, self.den * other.den)
    }
}

impl Div for Frac {
    type Output = Frac;
    fn div(self, other: Frac) -> Frac {
        Frac::new_reduced(self.num * other.den, self.den * other.num)
    }
}

impl num::Zero for Frac {
    fn zero() -> Frac {
        Frac::new(0, 1)
    }

    fn is_zero(&self) -> bool {
        self.num == 0 && self.den == 0
    }
}

impl num::One for Frac {
    fn one() -> Frac {
        Frac::new(1, 1)
    }
}

#[test]
fn operators() {
    let (a, b) =  (Frac::new(1, 2), Frac::new(12,15));
    assert_eq!(a+b, Frac::secure_new(13, 10).unwrap());
    assert_eq!(b-a, Frac::new(3, 10));
    assert_eq!(a-b, Frac::new(-3, 10));
    assert_eq!(a*b, Frac::new(2, 5));
    assert_eq!(a/b, Frac::new(5, 8));

    let (a, b) =  (Frac::new(1, 2), Frac::new(1,2));
    assert_eq!(a+b, num::One::one());
    assert_eq!(b-a, num::Zero::zero());
    assert_eq!(a-b, num::Zero::zero());
    assert_eq!(a*b, Frac::new(1, 4));
    assert_eq!(a/b, Frac::new(1, 1));
}

#[test]
fn first_perfect_numbers() {
    assert_eq!(perfect_numbers(8150), vec![6,28,496,8128]);
}
<commit_msg>remove unused imports<commit_after>// http://rosettacode.org/wiki/Arithmetic/Rational
#![feature(zero_one)]
use std::num::{Zero, One};
use std::fmt;
use std::cmp::Ordering;
use std::ops::{Add, Mul, Neg, Sub, Div};

fn main() {
    for p in perfect_numbers(1 << 19) {
        println!("{} is perfect", p);
    }
}

fn perfect_numbers(max: i64) -> Vec<i64> {
    let mut ret=Vec::new();
    for candidate in 2..max {
        let mut sum=Frac::secure_new(1, candidate).unwrap();
        let max2=((candidate as f64).sqrt().floor()) as i64;

        for factor in 2..max2 + 1 {
            if candidate % factor == 0 {
                sum = sum + Frac::new(1, factor) + Frac::new(factor, candidate);
            }
        }
        if sum == Frac::new(1,1) {ret.push(candidate);}
    }
    ret
}
#[derive(Copy, Clone)]
struct Frac {
    num: i64,
    den: i64
}

fn gcd(m: i64, n:i64) -> i64 {
    let mut t: i64;
    let (mut m, mut n)=(m.abs(), n.abs());
    while n>0 { t = n; n = m % n; m = t; }
    m
}

fn lcm(m: i64, n:i64) -> i64 {
    m.abs() / gcd(m, n) * n.abs()
}

impl Frac {
    // fails on den=0
    fn new(num: i64, den:i64) -> Frac {
        let (n, d) = match (num, den) {
            (0, _)          => (0, 0),
            (n, d) if d<0   => (-n, -d),
            a @ _           => a
        };

        Frac {num: n, den: d}
    }

    // does not fail (returns Err on den=0)
    fn secure_new(num: i64, den:i64) -> Result<Frac, String> {
        if den==0 {
            Err("Error: Division by zero".to_string())
        } else {
            Ok(Frac::new(num, den))
        }
    }

    // fails on den=0, returns frac already in its reduced form
    fn new_reduced(num: i64, den:i64) -> Frac {
        Frac::new(num, den).reduce()
    }

    // reduces the fraction to lowest terms
    fn reduce(mut self) -> Frac {
        match self {
            z @ Frac{num:0, den:0} => z,
            _         => {
                let gcd=gcd(self.num, self.den);
                self.num /= gcd;
                self.den /= gcd;
                self
            }
        }
    }
}

impl fmt::Debug for Frac {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.num, self.den) {
            (_,1) | (0,0) => write!(f, "{}", self.num),
            (_,_) => write!(f, "{}/{}", self.num, self.den)
        }
    }
}

impl PartialEq for Frac {
    fn eq(&self, other: &Frac) -> bool {
        let (red_a, red_b) = (self.reduce(), other.reduce());
        red_a.num==red_b.num && red_a.den==red_b.den
    }
}

impl Eq for Frac {}

impl PartialOrd for Frac {
    fn partial_cmp(&self, other: &Frac) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Frac {
    fn cmp(&self, other: &Frac) -> Ordering {
        (self.num * other.den).cmp(&(self.den * other.num))
    }
}

impl Neg for Frac {
    type Output = Frac;

    fn neg(self) -> Frac {
        Frac{num:-self.num, den:self.den}
    }
}

impl Add for Frac {
    type Output = Frac;
    fn add(self, other: Frac) -> Frac {
        let (a, b)=(self.reduce(), other.reduce());
        let m = lcm(a.den, b.den);

        let na = a.num * m / a.den;
        let nb = b.num * m / b.den;
        Frac::new_reduced(na + nb, m)
    }
}

impl Sub for Frac {
    type Output = Frac;
    fn sub(self, other: Frac) -> Frac {
        self + (- other)
    }
}

impl Mul for Frac {
    type Output = Frac;
    fn mul(self, other: Frac) -> Frac {
        Frac::new_reduced(self.num * other.num, self.den * other.den)
    }
}

impl Div for Frac {
    type Output = Frac;
    fn div(self, other: Frac) -> Frac {
        Frac::new_reduced(self.num * other.den, self.den * other.num)
    }
}

impl Zero for Frac {
    fn zero() -> Frac {
        Frac::new(0, 1)
    }
}

impl One for Frac {
    fn one() -> Frac {
        Frac::new(1, 1)
    }
}

#[test]
fn operators() {
    let (a, b) =  (Frac::new(1, 2), Frac::new(12,15));
    assert_eq!(a+b, Frac::secure_new(13, 10).unwrap());
    assert_eq!(b-a, Frac::new(3, 10));
    assert_eq!(a-b, Frac::new(-3, 10));
    assert_eq!(a*b, Frac::new(2, 5));
    assert_eq!(a/b, Frac::new(5, 8));

    let (a, b) =  (Frac::new(1, 2), Frac::new(1,2));
    assert_eq!(a+b, One::one());
    assert_eq!(b-a, Zero::zero());
    assert_eq!(a-b, Zero::zero());
    assert_eq!(a*b, Frac::new(1, 4));
    assert_eq!(a/b, Frac::new(1, 1));
}

#[test]
fn first_perfect_numbers() {
    assert_eq!(perfect_numbers(8150), vec![6,28,496,8128]);
}
<|endoftext|>


<commit_before>//! Lints concerned with the grouping of digits with underscores in integral or
//! floating-point literal expressions.

use rustc::lint::*;
use syntax::ast::*;
use syntax_pos;
use utils::{span_help_and_lint, snippet_opt, in_external_macro};

/// **What it does:** Warns if a long integral or floating-point constant does
/// not contain underscores.
///
/// **Why is this bad?** Reading long numbers is difficult without separators.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 61864918973511
/// ```
declare_lint! {
    pub UNREADABLE_LITERAL,
    Warn,
    "long integer literal without underscores"
}

/// **What it does:** Warns if an integral or floating-point constant is
/// grouped inconsistently with underscores.
///
/// **Why is this bad?** Readers may incorrectly interpret inconsistently
/// grouped digits.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 618_64_9189_73_511
/// ```
declare_lint! {
    pub INCONSISTENT_DIGIT_GROUPING,
    Warn,
    "integer literals with digits grouped inconsistently"
}

/// **What it does:** Warns if the digits of an integral or floating-point
/// constant are grouped into groups that
/// are too large.
///
/// **Why is this bad?** Negatively impacts readability.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 6186491_8973511
/// ```
declare_lint! {
    pub LARGE_DIGIT_GROUPS,
    Warn,
    "grouping digits into groups that are too large"
}

#[derive(Debug)]
enum Radix {
    Binary,
    Octal,
    Decimal,
    Hexadecimal,
}

impl Radix {
    /// Return a reasonable digit group size for this radix.
    pub fn suggest_grouping(&self) -> usize {
        match *self {
            Radix::Binary | Radix::Hexadecimal => 4,
            Radix::Octal | Radix::Decimal => 3,
        }
    }
}

#[derive(Debug)]
struct DigitInfo<'a> {
    /// Characters of a literal between the radix prefix and type suffix.
    pub digits: &'a str,
    /// Which radix the literal was represented in.
    pub radix: Radix,
    /// The radix prefix, if present.
    pub prefix: Option<&'a str>,
    /// The type suffix, including preceding underscore if present.
    pub suffix: Option<&'a str>,
    /// True for floating-point literals.
    pub float: bool,
}

impl<'a> DigitInfo<'a> {
    pub fn new(lit: &str, float: bool) -> DigitInfo {
        // Determine delimiter for radix prefix, if present, and radix.
        let radix = if lit.starts_with("0x") {
            Radix::Hexadecimal
        } else if lit.starts_with("0b") {
            Radix::Binary
        } else if lit.starts_with("0o") {
            Radix::Octal
        } else {
            Radix::Decimal
        };

        // Grab part of the literal after prefix, if present.
        let (prefix, sans_prefix) = if let Radix::Decimal = radix {
            (None, lit)
        } else {
            let (p, s) = lit.split_at(2);
            (Some(p), s)
        };

        let mut last_d = '\0';
        for (d_idx, d) in sans_prefix.char_indices() {
            if !float && (d == 'i' || d == 'u') || float && d == 'f' {
                let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
                let (digits, suffix) = sans_prefix.split_at(suffix_start);
                return DigitInfo {
                    digits: digits,
                    radix: radix,
                    prefix: prefix,
                    suffix: Some(suffix),
                    float: float,
                };
            }
            last_d = d
        }

        // No suffix found
        DigitInfo {
            digits: sans_prefix,
            radix: radix,
            prefix: prefix,
            suffix: None,
            float: float,
        }
    }

    /// Returns digits grouped in a sensible way.
    fn grouping_hint(&self) -> String {
        let group_size = self.radix.suggest_grouping();
        if self.digits.contains('.') {
            let mut parts = self.digits.split(".");
            let int_part_hint = parts
                .next()
                .unwrap()
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            let frac_part_hint = parts
                .next()
                .unwrap()
                .chars()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().collect())
                .collect::<Vec<String>>()
                .join("_");
            format!("{}.{}{}", int_part_hint, frac_part_hint, self.suffix.unwrap_or(""))
        } else {
            let hint = self.digits
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            format!("{}{}{}", self.prefix.unwrap_or(""), hint, self.suffix.unwrap_or(""))
        }
    }
}

enum WarningType {
    UnreadableLiteral,
    InconsistentDigitGrouping,
    LargeDigitGroups,
}

impl WarningType {
    pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
        match *self {
            WarningType::UnreadableLiteral => {
                span_help_and_lint(
                    cx,
                    UNREADABLE_LITERAL,
                    *span,
                    "long literal lacking separators",
                    &format!("consider: {}", grouping_hint),
                )
            },
            WarningType::LargeDigitGroups => {
                span_help_and_lint(
                    cx,
                    LARGE_DIGIT_GROUPS,
                    *span,
                    "digit groups should be smaller",
                    &format!("consider: {}", grouping_hint),
                )
            },
            WarningType::InconsistentDigitGrouping => {
                span_help_and_lint(
                    cx,
                    INCONSISTENT_DIGIT_GROUPING,
                    *span,
                    "digits grouped inconsistently by underscores",
                    &format!("consider: {}", grouping_hint),
                )
            },
        };
    }
}

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

impl LintPass for LiteralDigitGrouping {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS)
    }
}

impl EarlyLintPass for LiteralDigitGrouping {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        if in_external_macro(cx, expr.span) {
            return;
        }

        if let ExprKind::Lit(ref lit) = expr.node {
            self.check_lit(cx, lit)
        }
    }
}

impl LiteralDigitGrouping {
    fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
        // Lint integral literals.
        if_let_chain! {[
            let LitKind::Int(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digits = LiteralDigitGrouping::get_digits(&src, false);

            LiteralDigitGrouping::do_lint(digits, cx, &lit.span);
        }}

        // Lint floating-point literals.
        if_let_chain! {[
            let LitKind::Float(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digits: Vec<&str> = LiteralDigitGrouping::get_digits(&src, true)
                .split_terminator('.')
                .collect();

            // Lint integral and fractional parts separately, and then check consistency of digit
            // groups if both pass.
            if let Some(integral_group_size) = LiteralDigitGrouping::do_lint(digits[0], cx, &lit.span) {
                if digits.len() > 1 {
                    // Lint the fractional part of literal just like integral part, but reversed.
                    let fractional_part = &digits[1].chars().rev().collect::<String>();
                    if let Some(fractional_group_size) = LiteralDigitGrouping::do_lint(fractional_part, cx, &lit.span) {
                        let consistent = match (integral_group_size, fractional_group_size) {
                            // No groups on either side of decimal point - good to go.
                            (0, 0) => true,
                            // Integral part has grouped digits, fractional part does not.
                            (_, 0) => digits[1].len() <= integral_group_size,
                            // Fractional part has grouped digits, integral part does not.
                            (0, _) => digits[0].len() <= fractional_group_size,
                            // Both parts have grouped digits. Groups should be the same size.
                            (_, _) => integral_group_size == fractional_group_size,
                        };

                        if !consistent {
                            span_help_and_lint(cx, INCONSISTENT_DIGIT_GROUPING, lit.span,
                                           "digits grouped inconsistently by underscores",
                                           "consider making each group three or four digits");
                        }
                    }
                }
            }
        }}
    }

    /// Returns the digits of an integral or floating-point literal.
    fn get_digits(lit: &str, float: bool) -> &str {
        // Determine delimiter for radix prefix, if present.
        let mb_r = if lit.starts_with("0x") {
            Some('x')
        } else if lit.starts_with("0b") {
            Some('b')
        } else if lit.starts_with("0o") {
            Some('o')
        } else {
            None
        };

        let has_suffix = !float && (lit.contains('i') || lit.contains('u')) || float && lit.contains('f');

        // Grab part of literal between the radix prefix and type suffix.
        let mut digits = if let Some(r) = mb_r {
            lit.split(|c| c == 'i' || c == 'u' || c == r || (float && c == 'f')).nth(1).unwrap()
        } else {
            lit.split(|c| c == 'i' || c == 'u' || (float && c == 'f')).next().unwrap()
        };

        // If there was an underscore before type suffix, drop it.
        if has_suffix && digits.chars().last().unwrap() == '_' {
            digits = digits.split_at(digits.len() - 1).0;
        }

        digits
    }

    /// Performs lint on `digits` (no decimal point) and returns the group
    /// size on success or `WarningType` when emitting a warning.
    fn do_lint(digits: &str) -> Result<usize, WarningType> {
        // Grab underscore indices with respect to the units digit.
        let underscore_positions: Vec<usize> = digits
            .chars()
            .rev()
            .enumerate()
            .filter_map(|(idx, digit)| if digit == '_' { Some(idx) } else { None })
            .collect();

        if underscore_positions.is_empty() {
            // Check if literal needs underscores.
            if digits.len() > 4 {
                return Err(WarningType::UnreadableLiteral);
            } else {
                return Ok(0);
            }
        } else {
            // Check consistency and the sizes of the groups.
            let group_size = underscore_positions[0];
            let consistent = underscore_positions
                .windows(2)
                .all(|ps| ps[1] - ps[0] == group_size + 1)
                // number of digits to the left of the last group cannot be bigger than group size.
                && (digits.len() - underscore_positions.last().unwrap() <= group_size + 1);

            if !consistent {
                return Err(WarningType::InconsistentDigitGrouping);
            } else if group_size > 4 {
                return Err(WarningType::LargeDigitGroups);
            }
            return Ok(group_size);
        }
    }
}
<commit_msg>Use DigitInfo instead of get_digits().<commit_after>//! Lints concerned with the grouping of digits with underscores in integral or
//! floating-point literal expressions.

use rustc::lint::*;
use syntax::ast::*;
use syntax_pos;
use utils::{span_help_and_lint, snippet_opt, in_external_macro};

/// **What it does:** Warns if a long integral or floating-point constant does
/// not contain underscores.
///
/// **Why is this bad?** Reading long numbers is difficult without separators.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 61864918973511
/// ```
declare_lint! {
    pub UNREADABLE_LITERAL,
    Warn,
    "long integer literal without underscores"
}

/// **What it does:** Warns if an integral or floating-point constant is
/// grouped inconsistently with underscores.
///
/// **Why is this bad?** Readers may incorrectly interpret inconsistently
/// grouped digits.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 618_64_9189_73_511
/// ```
declare_lint! {
    pub INCONSISTENT_DIGIT_GROUPING,
    Warn,
    "integer literals with digits grouped inconsistently"
}

/// **What it does:** Warns if the digits of an integral or floating-point
/// constant are grouped into groups that
/// are too large.
///
/// **Why is this bad?** Negatively impacts readability.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 6186491_8973511
/// ```
declare_lint! {
    pub LARGE_DIGIT_GROUPS,
    Warn,
    "grouping digits into groups that are too large"
}

#[derive(Debug)]
enum Radix {
    Binary,
    Octal,
    Decimal,
    Hexadecimal,
}

impl Radix {
    /// Return a reasonable digit group size for this radix.
    pub fn suggest_grouping(&self) -> usize {
        match *self {
            Radix::Binary | Radix::Hexadecimal => 4,
            Radix::Octal | Radix::Decimal => 3,
        }
    }
}

#[derive(Debug)]
struct DigitInfo<'a> {
    /// Characters of a literal between the radix prefix and type suffix.
    pub digits: &'a str,
    /// Which radix the literal was represented in.
    pub radix: Radix,
    /// The radix prefix, if present.
    pub prefix: Option<&'a str>,
    /// The type suffix, including preceding underscore if present.
    pub suffix: Option<&'a str>,
    /// True for floating-point literals.
    pub float: bool,
}

impl<'a> DigitInfo<'a> {
    pub fn new(lit: &str, float: bool) -> DigitInfo {
        // Determine delimiter for radix prefix, if present, and radix.
        let radix = if lit.starts_with("0x") {
            Radix::Hexadecimal
        } else if lit.starts_with("0b") {
            Radix::Binary
        } else if lit.starts_with("0o") {
            Radix::Octal
        } else {
            Radix::Decimal
        };

        // Grab part of the literal after prefix, if present.
        let (prefix, sans_prefix) = if let Radix::Decimal = radix {
            (None, lit)
        } else {
            let (p, s) = lit.split_at(2);
            (Some(p), s)
        };

        let mut last_d = '\0';
        for (d_idx, d) in sans_prefix.char_indices() {
            if !float && (d == 'i' || d == 'u') || float && d == 'f' {
                let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
                let (digits, suffix) = sans_prefix.split_at(suffix_start);
                return DigitInfo {
                    digits: digits,
                    radix: radix,
                    prefix: prefix,
                    suffix: Some(suffix),
                    float: float,
                };
            }
            last_d = d
        }

        // No suffix found
        DigitInfo {
            digits: sans_prefix,
            radix: radix,
            prefix: prefix,
            suffix: None,
            float: float,
        }
    }

    /// Returns digits grouped in a sensible way.
    fn grouping_hint(&self) -> String {
        let group_size = self.radix.suggest_grouping();
        if self.digits.contains('.') {
            let mut parts = self.digits.split(".");
            let int_part_hint = parts
                .next()
                .unwrap()
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            let frac_part_hint = parts
                .next()
                .unwrap()
                .chars()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().collect())
                .collect::<Vec<String>>()
                .join("_");
            format!("{}.{}{}", int_part_hint, frac_part_hint, self.suffix.unwrap_or(""))
        } else {
            let hint = self.digits
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            format!("{}{}{}", self.prefix.unwrap_or(""), hint, self.suffix.unwrap_or(""))
        }
    }
}

enum WarningType {
    UnreadableLiteral,
    InconsistentDigitGrouping,
    LargeDigitGroups,
}

impl WarningType {
    pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
        match *self {
            WarningType::UnreadableLiteral => {
                span_help_and_lint(
                    cx,
                    UNREADABLE_LITERAL,
                    *span,
                    "long literal lacking separators",
                    &format!("consider: {}", grouping_hint),
                )
            },
            WarningType::LargeDigitGroups => {
                span_help_and_lint(
                    cx,
                    LARGE_DIGIT_GROUPS,
                    *span,
                    "digit groups should be smaller",
                    &format!("consider: {}", grouping_hint),
                )
            },
            WarningType::InconsistentDigitGrouping => {
                span_help_and_lint(
                    cx,
                    INCONSISTENT_DIGIT_GROUPING,
                    *span,
                    "digits grouped inconsistently by underscores",
                    &format!("consider: {}", grouping_hint),
                )
            },
        };
    }
}

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

impl LintPass for LiteralDigitGrouping {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS)
    }
}

impl EarlyLintPass for LiteralDigitGrouping {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        if in_external_macro(cx, expr.span) {
            return;
        }

        if let ExprKind::Lit(ref lit) = expr.node {
            self.check_lit(cx, lit)
        }
    }
}

impl LiteralDigitGrouping {
    fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
        // Lint integral literals.
        if_let_chain! {[
            let LitKind::Int(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, false);
            let _ = LiteralDigitGrouping::do_lint(digit_info.digits).map_err(|warning_type| {
                warning_type.display(&digit_info.grouping_hint(), cx, &lit.span)
            });
        }}

        // Lint floating-point literals.
        if_let_chain! {[
            let LitKind::Float(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, true);
            // Separate digits into integral and fractional parts.
            let parts: Vec<&str> = digit_info
                .digits
                .split_terminator('.')
                .collect();

            // Lint integral and fractional parts separately, and then check consistency of digit
            // groups if both pass.
            if let Some(integral_group_size) = LiteralDigitGrouping::do_lint(digits[0], cx, &lit.span) {
                if digits.len() > 1 {
                    // Lint the fractional part of literal just like integral part, but reversed.
                    let fractional_part = &digits[1].chars().rev().collect::<String>();
                    if let Some(fractional_group_size) = LiteralDigitGrouping::do_lint(fractional_part, cx, &lit.span) {
                        let consistent = match (integral_group_size, fractional_group_size) {
                            // No groups on either side of decimal point - good to go.
                            (0, 0) => true,
                            // Integral part has grouped digits, fractional part does not.
                            (_, 0) => digits[1].len() <= integral_group_size,
                            // Fractional part has grouped digits, integral part does not.
                            (0, _) => digits[0].len() <= fractional_group_size,
                            // Both parts have grouped digits. Groups should be the same size.
                            (_, _) => integral_group_size == fractional_group_size,
                        };

                        if !consistent {
                            span_help_and_lint(cx, INCONSISTENT_DIGIT_GROUPING, lit.span,
                                           "digits grouped inconsistently by underscores",
                                           "consider making each group three or four digits");
                        }
                    }
                }
            }
        }}
    }

        }
    }

    /// Performs lint on `digits` (no decimal point) and returns the group
    /// size on success or `WarningType` when emitting a warning.
    fn do_lint(digits: &str) -> Result<usize, WarningType> {
        // Grab underscore indices with respect to the units digit.
        let underscore_positions: Vec<usize> = digits
            .chars()
            .rev()
            .enumerate()
            .filter_map(|(idx, digit)| if digit == '_' { Some(idx) } else { None })
            .collect();

        if underscore_positions.is_empty() {
            // Check if literal needs underscores.
            if digits.len() > 4 {
                return Err(WarningType::UnreadableLiteral);
            } else {
                return Ok(0);
            }
        } else {
            // Check consistency and the sizes of the groups.
            let group_size = underscore_positions[0];
            let consistent = underscore_positions
                .windows(2)
                .all(|ps| ps[1] - ps[0] == group_size + 1)
                // number of digits to the left of the last group cannot be bigger than group size.
                && (digits.len() - underscore_positions.last().unwrap() <= group_size + 1);

            if !consistent {
                return Err(WarningType::InconsistentDigitGrouping);
            } else if group_size > 4 {
                return Err(WarningType::LargeDigitGroups);
            }
            return Ok(group_size);
        }
    }
}
<|endoftext|>