<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Simple counting bloom filters.

use fnv::FnvHasher;
use std::hash::{Hash, Hasher};

// The top 12 bits of the 32-bit hash value are not used by the bloom filter.
// Consumers may rely on this to pack hashes more efficiently.
pub const BLOOM_HASH_MASK: u32 = 0x00ffffff;
const KEY_SIZE: usize = 12;

const ARRAY_SIZE: usize = 1 << KEY_SIZE;
const KEY_MASK: u32 = (1 << KEY_SIZE) - 1;

/// A counting Bloom filter with 8-bit counters.  For now we assume
/// that having two hash functions is enough, but we may revisit that
/// decision later.
///
/// The filter uses an array with 2**KeySize entries.
///
/// Assuming a well-distributed hash function, a Bloom filter with
/// array size M containing N elements and
/// using k hash function has expected false positive rate exactly
///
/// $  (1 - (1 - 1/M)^{kN})^k  $
///
/// because each array slot has a
///
/// $  (1 - 1/M)^{kN}  $
///
/// chance of being 0, and the expected false positive rate is the
/// probability that all of the k hash functions will hit a nonzero
/// slot.
///
/// For reasonable assumptions (M large, kN large, which should both
/// hold if we're worried about false positives) about M and kN this
/// becomes approximately
///
/// $$  (1 - \exp(-kN/M))^k   $$
///
/// For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$,
/// or in other words
///
/// $$    N/M = -0.5 * \ln(1 - \sqrt(r))   $$
///
/// where r is the false positive rate.  This can be used to compute
/// the desired KeySize for a given load N and false positive rate r.
///
/// If N/M is assumed small, then the false positive rate can
/// further be approximated as 4*N^2/M^2.  So increasing KeySize by
/// 1, which doubles M, reduces the false positive rate by about a
/// factor of 4, and a false positive rate of 1% corresponds to
/// about M/N == 20.
///
/// What this means in practice is that for a few hundred keys using a
/// KeySize of 12 gives false positive rates on the order of 0.25-4%.
///
/// Similarly, using a KeySize of 10 would lead to a 4% false
/// positive rate for N == 100 and to quite bad false positive
/// rates for larger N.
pub struct BloomFilter {
    counters: [u8; ARRAY_SIZE],
}

impl Clone for BloomFilter {
    #[inline]
    fn clone(&self) -> BloomFilter {
        BloomFilter {
            counters: self.counters,
        }
    }
}

impl BloomFilter {
    /// Creates a new bloom filter.
    #[inline]
    pub fn new() -> BloomFilter {
        BloomFilter {
            counters: [0; ARRAY_SIZE],
        }
    }

    #[inline]
    fn first_slot(&self, hash: u32) -> &u8 {
        &self.counters[hash1(hash) as usize]
    }

    #[inline]
    fn first_mut_slot(&mut self, hash: u32) -> &mut u8 {
        &mut self.counters[hash1(hash) as usize]
    }

    #[inline]
    fn second_slot(&self, hash: u32) -> &u8 {
        &self.counters[hash2(hash) as usize]
    }

    #[inline]
    fn second_mut_slot(&mut self, hash: u32) -> &mut u8 {
        &mut self.counters[hash2(hash) as usize]
    }

    #[inline]
    pub fn clear(&mut self) {
        self.counters = [0; ARRAY_SIZE]
    }

    // Slow linear accessor to make sure the bloom filter is zeroed. This should
    // never be used in release builds.
    #[cfg(debug_assertions)]
    pub fn is_zeroed(&self) -> bool {
        self.counters.iter().all(|x| *x == 0)
    }

    #[cfg(not(debug_assertions))]
    pub fn is_zeroed(&self) -> bool {
        unreachable!()
    }

    #[inline]
    pub fn insert_hash(&mut self, hash: u32) {
        {
            let slot1 = self.first_mut_slot(hash);
            if !full(slot1) {
                *slot1 += 1
            }
        }
        {
            let slot2 = self.second_mut_slot(hash);
            if !full(slot2) {
                *slot2 += 1
            }
        }
    }

    /// Inserts an item into the bloom filter.
    #[inline]
    pub fn insert<T: Hash>(&mut self, elem: &T) {
        self.insert_hash(hash(elem))
    }

    #[inline]
    pub fn remove_hash(&mut self, hash: u32) {
        {
            let slot1 = self.first_mut_slot(hash);
            if !full(slot1) {
                *slot1 -= 1
            }
        }
        {
            let slot2 = self.second_mut_slot(hash);
            if !full(slot2) {
                *slot2 -= 1
            }
        }
    }

    /// Removes an item from the bloom filter.
    #[inline]
    pub fn remove<T: Hash>(&mut self, elem: &T) {
        self.remove_hash(hash(elem))
    }

    #[inline]
    pub fn might_contain_hash(&self, hash: u32) -> bool {
        *self.first_slot(hash) != 0 && *self.second_slot(hash) != 0
    }

    /// Check whether the filter might contain an item.  This can
    /// sometimes return true even if the item is not in the filter,
    /// but will never return false for items that are actually in the
    /// filter.
    #[inline]
    pub fn might_contain<T: Hash>(&self, elem: &T) -> bool {
        self.might_contain_hash(hash(elem))
    }
}

#[inline]
fn full(slot: &u8) -> bool {
    *slot == 0xff
}

fn hash<T: Hash>(elem: &T) -> u32 {
    let mut hasher = FnvHasher::default();
    elem.hash(&mut hasher);
    let hash: u64 = hasher.finish();
    (hash >> 32) as u32 ^ (hash as u32)
}

#[inline]
fn hash1(hash: u32) -> u32 {
    hash & KEY_MASK
}

#[inline]
fn hash2(hash: u32) -> u32 {
    (hash >> KEY_SIZE) & KEY_MASK
}

#[test]
fn create_and_insert_some_stuff() {
    let mut bf = BloomFilter::new();

    for i in 0_usize .. 1000 {
        bf.insert(&i);
    }

    for i in 0_usize .. 1000 {
        assert!(bf.might_contain(&i));
    }

    let false_positives =
        (1001_usize .. 2000).filter(|i| bf.might_contain(i)).count();

    assert!(false_positives < 150, "{} is not < 150", false_positives); // 15%.

    for i in 0_usize .. 100 {
        bf.remove(&i);
    }

    for i in 100_usize .. 1000 {
        assert!(bf.might_contain(&i));
    }

    let false_positives = (0_usize .. 100).filter(|i| bf.might_contain(i)).count();

    assert!(false_positives < 20, "{} is not < 20", false_positives); // 20%.

    bf.clear();

    for i in 0_usize .. 2000 {
        assert!(!bf.might_contain(&i));
    }
}

#[cfg(feature = "unstable")]
#[cfg(test)]
mod bench {
    extern crate test;
    use super::BloomFilter;

    #[derive(Default)]
    struct HashGenerator(u32);

    impl HashGenerator {
        fn next(&mut self) -> u32 {
            // Each hash is split into two twelve-bit segments, which are used
            // as an index into an array. We increment each by 64 so that we
            // hit the next cache line, and then another 1 so that our wrapping
            // behavior leads us to different entries each time.
            //
            // Trying to simulate cold caches is rather difficult with the cargo
            // benchmarking setup, so it may all be moot depending on the number
            // of iterations that end up being run. But we might as well.
            self.0 += (65) + (65 << super::KEY_SIZE);
            self.0
        }
    }

    #[bench]
    fn create_insert_1000_remove_100_lookup_100(b: &mut test::Bencher) {
        b.iter(|| {
            let mut gen1 = HashGenerator::default();
            let mut gen2 = HashGenerator::default();
            let mut bf = BloomFilter::new();
            for _ in 0_usize .. 1000 {
                bf.insert_hash(gen1.next());
            }
            for _ in 0_usize .. 100 {
                bf.remove_hash(gen2.next());
            }
            for _ in 100_usize .. 200 {
                test::black_box(bf.might_contain_hash(gen2.next()));
            }
        });
    }

    #[bench]
    fn might_contain_10(b: &mut test::Bencher) {
        let bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        b.iter(|| for _ in 0..10 {
            test::black_box(bf.might_contain_hash(gen.next()));
        });
    }

    #[bench]
    fn clear(b: &mut test::Bencher) {
        let mut bf = Box::new(BloomFilter::new());
        b.iter(|| test::black_box(&mut bf).clear());
    }

    #[bench]
    fn insert_10(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        b.iter(|| for _ in 0..10 {
            test::black_box(bf.insert_hash(gen.next()));
        });
    }

    #[bench]
    fn remove_10(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        // Note: this will underflow, and that's ok.
        b.iter(|| for _ in 0..10 {
            bf.remove_hash(gen.next())
        });
    }
}
<commit_msg>Auto merge of #17487 - MattWis:patch-1, r=emilio<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Simple counting bloom filters.

use fnv::FnvHasher;
use std::hash::{Hash, Hasher};

// The top 8 bits of the 32-bit hash value are not used by the bloom filter.
// Consumers may rely on this to pack hashes more efficiently.
pub const BLOOM_HASH_MASK: u32 = 0x00ffffff;
const KEY_SIZE: usize = 12;

const ARRAY_SIZE: usize = 1 << KEY_SIZE;
const KEY_MASK: u32 = (1 << KEY_SIZE) - 1;

/// A counting Bloom filter with 8-bit counters.  For now we assume
/// that having two hash functions is enough, but we may revisit that
/// decision later.
///
/// The filter uses an array with 2**KeySize entries.
///
/// Assuming a well-distributed hash function, a Bloom filter with
/// array size M containing N elements and
/// using k hash function has expected false positive rate exactly
///
/// $  (1 - (1 - 1/M)^{kN})^k  $
///
/// because each array slot has a
///
/// $  (1 - 1/M)^{kN}  $
///
/// chance of being 0, and the expected false positive rate is the
/// probability that all of the k hash functions will hit a nonzero
/// slot.
///
/// For reasonable assumptions (M large, kN large, which should both
/// hold if we're worried about false positives) about M and kN this
/// becomes approximately
///
/// $$  (1 - \exp(-kN/M))^k   $$
///
/// For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$,
/// or in other words
///
/// $$    N/M = -0.5 * \ln(1 - \sqrt(r))   $$
///
/// where r is the false positive rate.  This can be used to compute
/// the desired KeySize for a given load N and false positive rate r.
///
/// If N/M is assumed small, then the false positive rate can
/// further be approximated as 4*N^2/M^2.  So increasing KeySize by
/// 1, which doubles M, reduces the false positive rate by about a
/// factor of 4, and a false positive rate of 1% corresponds to
/// about M/N == 20.
///
/// What this means in practice is that for a few hundred keys using a
/// KeySize of 12 gives false positive rates on the order of 0.25-4%.
///
/// Similarly, using a KeySize of 10 would lead to a 4% false
/// positive rate for N == 100 and to quite bad false positive
/// rates for larger N.
pub struct BloomFilter {
    counters: [u8; ARRAY_SIZE],
}

impl Clone for BloomFilter {
    #[inline]
    fn clone(&self) -> BloomFilter {
        BloomFilter {
            counters: self.counters,
        }
    }
}

impl BloomFilter {
    /// Creates a new bloom filter.
    #[inline]
    pub fn new() -> BloomFilter {
        BloomFilter {
            counters: [0; ARRAY_SIZE],
        }
    }

    #[inline]
    fn first_slot(&self, hash: u32) -> &u8 {
        &self.counters[hash1(hash) as usize]
    }

    #[inline]
    fn first_mut_slot(&mut self, hash: u32) -> &mut u8 {
        &mut self.counters[hash1(hash) as usize]
    }

    #[inline]
    fn second_slot(&self, hash: u32) -> &u8 {
        &self.counters[hash2(hash) as usize]
    }

    #[inline]
    fn second_mut_slot(&mut self, hash: u32) -> &mut u8 {
        &mut self.counters[hash2(hash) as usize]
    }

    #[inline]
    pub fn clear(&mut self) {
        self.counters = [0; ARRAY_SIZE]
    }

    // Slow linear accessor to make sure the bloom filter is zeroed. This should
    // never be used in release builds.
    #[cfg(debug_assertions)]
    pub fn is_zeroed(&self) -> bool {
        self.counters.iter().all(|x| *x == 0)
    }

    #[cfg(not(debug_assertions))]
    pub fn is_zeroed(&self) -> bool {
        unreachable!()
    }

    #[inline]
    pub fn insert_hash(&mut self, hash: u32) {
        {
            let slot1 = self.first_mut_slot(hash);
            if !full(slot1) {
                *slot1 += 1
            }
        }
        {
            let slot2 = self.second_mut_slot(hash);
            if !full(slot2) {
                *slot2 += 1
            }
        }
    }

    /// Inserts an item into the bloom filter.
    #[inline]
    pub fn insert<T: Hash>(&mut self, elem: &T) {
        self.insert_hash(hash(elem))
    }

    #[inline]
    pub fn remove_hash(&mut self, hash: u32) {
        {
            let slot1 = self.first_mut_slot(hash);
            if !full(slot1) {
                *slot1 -= 1
            }
        }
        {
            let slot2 = self.second_mut_slot(hash);
            if !full(slot2) {
                *slot2 -= 1
            }
        }
    }

    /// Removes an item from the bloom filter.
    #[inline]
    pub fn remove<T: Hash>(&mut self, elem: &T) {
        self.remove_hash(hash(elem))
    }

    #[inline]
    pub fn might_contain_hash(&self, hash: u32) -> bool {
        *self.first_slot(hash) != 0 && *self.second_slot(hash) != 0
    }

    /// Check whether the filter might contain an item.  This can
    /// sometimes return true even if the item is not in the filter,
    /// but will never return false for items that are actually in the
    /// filter.
    #[inline]
    pub fn might_contain<T: Hash>(&self, elem: &T) -> bool {
        self.might_contain_hash(hash(elem))
    }
}

#[inline]
fn full(slot: &u8) -> bool {
    *slot == 0xff
}

fn hash<T: Hash>(elem: &T) -> u32 {
    let mut hasher = FnvHasher::default();
    elem.hash(&mut hasher);
    let hash: u64 = hasher.finish();
    (hash >> 32) as u32 ^ (hash as u32)
}

#[inline]
fn hash1(hash: u32) -> u32 {
    hash & KEY_MASK
}

#[inline]
fn hash2(hash: u32) -> u32 {
    (hash >> KEY_SIZE) & KEY_MASK
}

#[test]
fn create_and_insert_some_stuff() {
    let mut bf = BloomFilter::new();

    for i in 0_usize .. 1000 {
        bf.insert(&i);
    }

    for i in 0_usize .. 1000 {
        assert!(bf.might_contain(&i));
    }

    let false_positives =
        (1001_usize .. 2000).filter(|i| bf.might_contain(i)).count();

    assert!(false_positives < 150, "{} is not < 150", false_positives); // 15%.

    for i in 0_usize .. 100 {
        bf.remove(&i);
    }

    for i in 100_usize .. 1000 {
        assert!(bf.might_contain(&i));
    }

    let false_positives = (0_usize .. 100).filter(|i| bf.might_contain(i)).count();

    assert!(false_positives < 20, "{} is not < 20", false_positives); // 20%.

    bf.clear();

    for i in 0_usize .. 2000 {
        assert!(!bf.might_contain(&i));
    }
}

#[cfg(feature = "unstable")]
#[cfg(test)]
mod bench {
    extern crate test;
    use super::BloomFilter;

    #[derive(Default)]
    struct HashGenerator(u32);

    impl HashGenerator {
        fn next(&mut self) -> u32 {
            // Each hash is split into two twelve-bit segments, which are used
            // as an index into an array. We increment each by 64 so that we
            // hit the next cache line, and then another 1 so that our wrapping
            // behavior leads us to different entries each time.
            //
            // Trying to simulate cold caches is rather difficult with the cargo
            // benchmarking setup, so it may all be moot depending on the number
            // of iterations that end up being run. But we might as well.
            self.0 += (65) + (65 << super::KEY_SIZE);
            self.0
        }
    }

    #[bench]
    fn create_insert_1000_remove_100_lookup_100(b: &mut test::Bencher) {
        b.iter(|| {
            let mut gen1 = HashGenerator::default();
            let mut gen2 = HashGenerator::default();
            let mut bf = BloomFilter::new();
            for _ in 0_usize .. 1000 {
                bf.insert_hash(gen1.next());
            }
            for _ in 0_usize .. 100 {
                bf.remove_hash(gen2.next());
            }
            for _ in 100_usize .. 200 {
                test::black_box(bf.might_contain_hash(gen2.next()));
            }
        });
    }

    #[bench]
    fn might_contain_10(b: &mut test::Bencher) {
        let bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        b.iter(|| for _ in 0..10 {
            test::black_box(bf.might_contain_hash(gen.next()));
        });
    }

    #[bench]
    fn clear(b: &mut test::Bencher) {
        let mut bf = Box::new(BloomFilter::new());
        b.iter(|| test::black_box(&mut bf).clear());
    }

    #[bench]
    fn insert_10(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        b.iter(|| for _ in 0..10 {
            test::black_box(bf.insert_hash(gen.next()));
        });
    }

    #[bench]
    fn remove_10(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new();
        let mut gen = HashGenerator::default();
        // Note: this will underflow, and that's ok.
        b.iter(|| for _ in 0..10 {
            bf.remove_hash(gen.next())
        });
    }
}
<|endoftext|>


<commit_before>use fs;
use std::path::{Path, PathBuf};
use std::slice::Iter;
use std::ops::{Index, IndexMut};
use xi_rope::Rope;

/// A `Buffer` represents the in-process data structures of an open file. This includes the buffer
/// contents and certain tracking information to support editing operations. It does not include
/// things to do with display: a file can be opened in a `Buffer` without being currently displayed,
/// in fact it need never be displayed at all. On the other hand, a `Buffer` may be displayed in
/// several different `Windows` simultaneously.
pub struct Buffer {
    /// If this Buffer corresponds to a file, the name of the file.
    pub filename: Option<PathBuf>,
    /// The data in the buffer, expressed as a Rope structure.
    data: Rope,
    pub is_changed: bool
    // created_time_utc: Tm
    // last_accessed_time_utc: Tm
    // is_changed: bool
    // default_title: String   title to be used when there is only 1 view on a buffer. The filename
    //                         or Untitled<1>
    // TODO: Need title allocation function
}

impl Buffer {
    /// Creates a new empty buffer.
    pub fn new() -> Buffer {
        Buffer {
            filename: None,
            data: Rope::from(""),
            is_changed: false
        }
    }

    pub fn open_file(filename: &Path) -> Option<Buffer> {
        match fs::load_to_string(filename) {
            Ok(contents) => {
                Some(Buffer {
                    filename: Some(PathBuf::from(filename)),
                    data: Rope::from(contents),
                    is_changed: false
                })
            },
            Err(e) => { warn!("{}", e); None }
        }

    }
}

/// Manages the collection of open buffers.
/// If a buffer is backed by a file, a second buffer on that file cannot be created.
pub struct BufferCollection {
    buffers: Vec<Buffer>
}

impl BufferCollection {
    pub fn new() -> BufferCollection {
        BufferCollection {
            buffers: Vec::new()
        }
    }

    pub fn len(&self) -> usize {
        self.buffers.len()
    }

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

    pub fn iter(&self) -> Iter<Buffer> {
        self.buffers.iter()
    }

    pub fn add(&mut self, buffer: Buffer) {
        self.buffers.push(buffer)
    }

    pub fn find_by_filename<P : AsRef<Path>>(&mut self, filename: P) -> Option<&mut Buffer> {
        let pb = Some(filename.as_ref().to_path_buf());
        self.buffers.iter_mut().find(|&ref x| x.filename == pb)
    }
}

impl Index<usize> for BufferCollection {
    type Output = Buffer;

    fn index(&self, index: usize) -> &Buffer {
        &self.buffers[index]
    }
}

impl IndexMut<usize> for BufferCollection {
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Buffer {
        &mut self.buffers[index]
    }
}

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

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

    #[test]
    fn add_works() {
        let mut bc = BufferCollection::new();
        let mut b = Buffer::new();
        b.filename = Some(PathBuf::from("a"));
        bc.add(b);

        let b2 = &bc[0];
        assert_eq!(b2.filename, Some(PathBuf::from("a")));
    }
}
<commit_msg>Buffers: find_by_filename now does not not allocate.<commit_after>use fs;
use std::path::{Path, PathBuf};
use std::slice::Iter;
use std::ops::{Index, IndexMut};
use xi_rope::Rope;

/// A `Buffer` represents the in-process data structures of an open file. This includes the buffer
/// contents and certain tracking information to support editing operations. It does not include
/// things to do with display: a file can be opened in a `Buffer` without being currently displayed,
/// in fact it need never be displayed at all. On the other hand, a `Buffer` may be displayed in
/// several different `Windows` simultaneously.
pub struct Buffer {
    /// If this Buffer corresponds to a file, the name of the file.
    pub filename: Option<PathBuf>,
    /// The data in the buffer, expressed as a Rope structure.
    data: Rope,
    pub is_changed: bool
    // created_time_utc: Tm
    // last_accessed_time_utc: Tm
    // is_changed: bool
    // default_title: String   title to be used when there is only 1 view on a buffer. The filename
    //                         or Untitled<1>
    // TODO: Need title allocation function
}

impl Buffer {
    /// Creates a new empty buffer.
    pub fn new() -> Buffer {
        Buffer {
            filename: None,
            data: Rope::from(""),
            is_changed: false
        }
    }

    pub fn open_file(filename: &Path) -> Option<Buffer> {
        match fs::load_to_string(filename) {
            Ok(contents) => {
                Some(Buffer {
                    filename: Some(PathBuf::from(filename)),
                    data: Rope::from(contents),
                    is_changed: false
                })
            },
            Err(e) => { warn!("{}", e); None }
        }

    }
}

/// Manages the collection of open buffers.
/// If a buffer is backed by a file, a second buffer on that file cannot be created.
pub struct BufferCollection {
    buffers: Vec<Buffer>
}

impl BufferCollection {
    pub fn new() -> BufferCollection {
        BufferCollection {
            buffers: Vec::new()
        }
    }

    pub fn len(&self) -> usize {
        self.buffers.len()
    }

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

    pub fn iter(&self) -> Iter<Buffer> {
        self.buffers.iter()
    }

    pub fn add(&mut self, buffer: Buffer) {
        self.buffers.push(buffer)
    }

    pub fn find_by_filename<P : AsRef<Path>>(&mut self, filename: P) -> Option<&mut Buffer> {
        self.buffers.iter_mut().find(|b| b.filename.as_ref().map_or(false, |f| f == filename.as_ref()))
    }
}

impl Index<usize> for BufferCollection {
    type Output = Buffer;

    fn index(&self, index: usize) -> &Buffer {
        &self.buffers[index]
    }
}

impl IndexMut<usize> for BufferCollection {
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Buffer {
        &mut self.buffers[index]
    }
}

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

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

    #[test]
    fn add_works() {
        let mut bc = BufferCollection::new();
        let mut b = Buffer::new();
        b.filename = Some(PathBuf::from("a"));
        bc.add(b);

        let b2 = &bc[0];
        assert_eq!(b2.filename, Some(PathBuf::from("a")));
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::HTMLCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root, MutNullableHeap};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::trace::JSTraceable;
use dom::bindings::xmlname::namespace_from_domstring;
use dom::element::Element;
use dom::node::{Node, FollowingNodeIterator, PrecedingNodeIterator};
use dom::window::Window;
use std::ascii::AsciiExt;
use std::cell::Cell;
use string_cache::{Atom, Namespace, QualName};
use util::str::{DOMString, split_html_space_chars};

pub trait CollectionFilter : JSTraceable {
    fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool;
}

// An optional u32, using maxint to represent None.
// It would be nicer just to use Option<u32> for this, but that would produce word
// alignment issues since Option<u32> uses 33 bits.
#[derive(Clone, Copy, JSTraceable, HeapSizeOf)]
struct OptionU32 {
    bits: u32,
}

impl OptionU32 {
    fn to_option(self) -> Option<u32> {
        if self.bits == u32::max_value() {
            None
        } else {
            Some(self.bits)
        }
    }

    fn some(bits: u32) -> OptionU32 {
        assert!(bits != u32::max_value());
        OptionU32 { bits: bits }
    }

    fn none() -> OptionU32 {
        OptionU32 { bits: u32::max_value() }
    }
}

#[dom_struct]
pub struct HTMLCollection {
    reflector_: Reflector,
    root: JS<Node>,
    #[ignore_heap_size_of = "Contains a trait object; can't measure due to #6870"]
    filter: Box<CollectionFilter + 'static>,
    // We cache the version of the root node and all its decendents,
    // the length of the collection, and a cursor into the collection.
    // FIXME: make the cached cursor element a weak pointer
    cached_version: Cell<u64>,
    cached_cursor_element: MutNullableHeap<JS<Element>>,
    cached_cursor_index: Cell<OptionU32>,
    cached_length: Cell<OptionU32>,
}

impl HTMLCollection {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(root: &Node, filter: Box<CollectionFilter + 'static>) -> HTMLCollection {
        HTMLCollection {
            reflector_: Reflector::new(),
            root: JS::from_ref(root),
            filter: filter,
            // Default values for the cache
            cached_version: Cell::new(root.get_inclusive_descendants_version()),
            cached_cursor_element: MutNullableHeap::new(None),
            cached_cursor_index: Cell::new(OptionU32::none()),
            cached_length: Cell::new(OptionU32::none()),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(window: &Window, root: &Node, filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        reflect_dom_object(box HTMLCollection::new_inherited(root, filter),
                           GlobalRef::Window(window), HTMLCollectionBinding::Wrap)
    }

    pub fn create(window: &Window, root: &Node,
                  filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        HTMLCollection::new(window, root, filter)
    }

    fn validate_cache(&self) {
        // Clear the cache if the root version is different from our cached version
        let cached_version = self.cached_version.get();
        let curr_version = self.root.get_inclusive_descendants_version();
        if curr_version != cached_version {
            // Default values for the cache
            self.cached_version.set(curr_version);
            self.cached_cursor_element.set(None);
            self.cached_length.set(OptionU32::none());
            self.cached_cursor_index.set(OptionU32::none());
        }
    }

    fn set_cached_cursor(&self, index: u32, element: Option<Root<Element>>) -> Option<Root<Element>> {
        if let Some(element) = element {
            self.cached_cursor_index.set(OptionU32::some(index));
            self.cached_cursor_element.set(Some(element.r()));
            Some(element)
        } else {
            None
        }
    }

    pub fn by_tag_name(window: &Window, root: &Node, mut tag: DOMString)
                       -> Root<HTMLCollection> {
        let tag_atom = Atom::from(&*tag);
        tag.make_ascii_lowercase();
        let ascii_lower_tag = Atom::from(tag); // FIXME(ajeffrey): don't clone atom if it was already lowercased.
        HTMLCollection::by_atomic_tag_name(window, root, tag_atom, ascii_lower_tag)
    }

    pub fn by_atomic_tag_name(window: &Window, root: &Node, tag_atom: Atom, ascii_lower_tag: Atom)
                       -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameFilter {
            tag: Atom,
            ascii_lower_tag: Atom,
        }
        impl CollectionFilter for TagNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                if self.tag == atom!("*") {
                    true
                } else if elem.html_element_in_html_document() {
                    *elem.local_name() == self.ascii_lower_tag
                } else {
                    *elem.local_name() == self.tag
                }
            }
        }
        let filter = TagNameFilter {
            tag: tag_atom,
            ascii_lower_tag: ascii_lower_tag,
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString,
                          maybe_ns: Option<DOMString>) -> Root<HTMLCollection> {
        let local = Atom::from(tag);
        let ns = namespace_from_domstring(maybe_ns);
        let qname = QualName::new(ns, local);
        HTMLCollection::by_qual_tag_name(window, root, qname)
    }

    pub fn by_qual_tag_name(window: &Window, root: &Node, qname: QualName) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameNSFilter {
            qname: QualName
        }
        impl CollectionFilter for TagNameNSFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                    ((self.qname.ns == Namespace(atom!("*"))) || (self.qname.ns == *elem.namespace()))
                &&  ((self.qname.local == atom!("*")) || (self.qname.local == *elem.local_name()))
            }
        }
        let filter = TagNameNSFilter {
            qname: qname
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_class_name(window: &Window, root: &Node, classes: DOMString)
                         -> Root<HTMLCollection> {
        let class_atoms = split_html_space_chars(&classes).map(Atom::from).collect();
        HTMLCollection::by_atomic_class_name(window, root, class_atoms)
    }

    pub fn by_atomic_class_name(window: &Window, root: &Node, classes: Vec<Atom>)
                         -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ClassNameFilter {
            classes: Vec<Atom>
        }
        impl CollectionFilter for ClassNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                self.classes.iter().all(|class| elem.has_class(class))
            }
        }
        let filter = ClassNameFilter {
            classes: classes
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn children(window: &Window, root: &Node) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ElementChildFilter;
        impl CollectionFilter for ElementChildFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                root.is_parent_of(elem.upcast())
            }
        }
        HTMLCollection::create(window, root, box ElementChildFilter)
    }

    pub fn elements_iter_after(&self, after: &Node) -> HTMLCollectionElementsIter {
        // Iterate forwards from a node.
        HTMLCollectionElementsIter {
            node_iter: after.following_nodes(&self.root),
            root: Root::from_ref(&self.root),
            filter: &self.filter,
        }
    }

    pub fn elements_iter(&self) -> HTMLCollectionElementsIter {
        // Iterate forwards from the root.
        self.elements_iter_after(&*self.root)
    }

    pub fn elements_iter_before(&self, before: &Node) -> HTMLCollectionElementsRevIter {
        // Iterate backwards from a node.
        HTMLCollectionElementsRevIter {
            node_iter: before.preceding_nodes(&self.root),
            root: Root::from_ref(&self.root),
            filter: &self.filter,
        }
    }

}

// TODO: Make this generic, and avoid code duplication
pub struct HTMLCollectionElementsIter<'a> {
    node_iter: FollowingNodeIterator,
    root: Root<Node>,
    filter: &'a Box<CollectionFilter>,
}

impl<'a> Iterator for HTMLCollectionElementsIter<'a> {
    type Item = Root<Element>;

    fn next(&mut self) -> Option<Self::Item> {
        let filter = &self.filter;
        let root = &self.root;
        self.node_iter.by_ref()
                      .filter_map(Root::downcast)
                      .filter(|element| filter.filter(&element, root))
                      .next()
   }
}

pub struct HTMLCollectionElementsRevIter<'a> {
    node_iter: PrecedingNodeIterator,
    root: Root<Node>,
    filter: &'a Box<CollectionFilter>,
}

impl<'a> Iterator for HTMLCollectionElementsRevIter<'a> {
    type Item = Root<Element>;

    fn next(&mut self) -> Option<Self::Item> {
        let filter = &self.filter;
        let root = &self.root;
        self.node_iter.by_ref()
            .filter_map(Root::downcast)
            .filter(|element| filter.filter(&element, root))
            .next()
    }
}

impl HTMLCollectionMethods for HTMLCollection {
    // https://dom.spec.whatwg.org/#dom-htmlcollection-length
    fn Length(&self) -> u32 {
        self.validate_cache();

        if let Some(cached_length) = self.cached_length.get().to_option() {
            // Cache hit
            cached_length
        } else {
            // Cache miss, calculate the length
            let length = self.elements_iter().count() as u32;
            self.cached_length.set(OptionU32::some(length));
            length
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn Item(&self, index: u32) -> Option<Root<Element>> {
        self.validate_cache();

        if let Some(element) = self.cached_cursor_element.get() {
            // Cache hit, the cursor element is set
            if let Some(cached_index) = self.cached_cursor_index.get().to_option() {
                if cached_index == index {
                    // The cursor is the element we're looking for
                    Some(element)
                } else if cached_index < index {
                    // The cursor is before the element we're looking for
                    // Iterate forwards, starting at the cursor.
                    let offset = index - (cached_index + 1);
                    let node: Root<Node> = Root::upcast(element);
                    self.set_cached_cursor(index, self.elements_iter_after(node.r()).nth(offset as usize))
                } else {
                    // The cursor is after the element we're looking for
                    // Iterate backwards, starting at the cursor.
                    let offset = cached_index - (index + 1);
                    let node: Root<Node> = Root::upcast(element);
                    self.set_cached_cursor(index, self.elements_iter_before(node.r()).nth(offset as usize))
                }
            } else {
                // Cache miss
                // Iterate forwards through all the nodes
                self.set_cached_cursor(index, self.elements_iter().nth(index as usize))
            }
        } else {
            // Cache miss
            // Iterate forwards through all the nodes
            self.set_cached_cursor(index, self.elements_iter().nth(index as usize))
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
    fn NamedItem(&self, key: DOMString) -> Option<Root<Element>> {
        // Step 1.
        if key.is_empty() {
            return None;
        }

        // Step 2.
        self.elements_iter().find(|elem| {
            elem.get_string_attribute(&atom!("id")) == key ||
            (elem.namespace() == &ns!(html) && elem.get_string_attribute(&atom!("name")) == key)
        })
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.Item(index);
        *found = maybe_elem.is_some();
        maybe_elem
    }

    // check-tidy: no specs after this line
    fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.NamedItem(name);
        *found = maybe_elem.is_some();
        maybe_elem
    }

    // https://dom.spec.whatwg.org/#interface-htmlcollection
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        // Step 1
        let mut result = vec![];

        // Step 2
        for elem in self.elements_iter() {
            // Step 2.1
            let id_attr = elem.get_string_attribute(&atom!("id"));
            if !id_attr.is_empty() && !result.contains(&id_attr) {
                result.push(id_attr)
            }
            // Step 2.2
            let name_attr = elem.get_string_attribute(&atom!("name"));
            if !name_attr.is_empty() && !result.contains(&name_attr) && *elem.namespace() == ns!(html) {
                result.push(name_attr)
            }
        }

        // Step 3
        result
    }
}
<commit_msg>Remove HTMLCollectionElementsRevIter<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::HTMLCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root, MutNullableHeap};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::trace::JSTraceable;
use dom::bindings::xmlname::namespace_from_domstring;
use dom::element::Element;
use dom::node::Node;
use dom::window::Window;
use std::ascii::AsciiExt;
use std::cell::Cell;
use string_cache::{Atom, Namespace, QualName};
use util::str::{DOMString, split_html_space_chars};

pub trait CollectionFilter : JSTraceable {
    fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool;
}

// An optional u32, using maxint to represent None.
// It would be nicer just to use Option<u32> for this, but that would produce word
// alignment issues since Option<u32> uses 33 bits.
#[derive(Clone, Copy, JSTraceable, HeapSizeOf)]
struct OptionU32 {
    bits: u32,
}

impl OptionU32 {
    fn to_option(self) -> Option<u32> {
        if self.bits == u32::max_value() {
            None
        } else {
            Some(self.bits)
        }
    }

    fn some(bits: u32) -> OptionU32 {
        assert!(bits != u32::max_value());
        OptionU32 { bits: bits }
    }

    fn none() -> OptionU32 {
        OptionU32 { bits: u32::max_value() }
    }
}

#[dom_struct]
pub struct HTMLCollection {
    reflector_: Reflector,
    root: JS<Node>,
    #[ignore_heap_size_of = "Contains a trait object; can't measure due to #6870"]
    filter: Box<CollectionFilter + 'static>,
    // We cache the version of the root node and all its decendents,
    // the length of the collection, and a cursor into the collection.
    // FIXME: make the cached cursor element a weak pointer
    cached_version: Cell<u64>,
    cached_cursor_element: MutNullableHeap<JS<Element>>,
    cached_cursor_index: Cell<OptionU32>,
    cached_length: Cell<OptionU32>,
}

impl HTMLCollection {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(root: &Node, filter: Box<CollectionFilter + 'static>) -> HTMLCollection {
        HTMLCollection {
            reflector_: Reflector::new(),
            root: JS::from_ref(root),
            filter: filter,
            // Default values for the cache
            cached_version: Cell::new(root.get_inclusive_descendants_version()),
            cached_cursor_element: MutNullableHeap::new(None),
            cached_cursor_index: Cell::new(OptionU32::none()),
            cached_length: Cell::new(OptionU32::none()),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(window: &Window, root: &Node, filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        reflect_dom_object(box HTMLCollection::new_inherited(root, filter),
                           GlobalRef::Window(window), HTMLCollectionBinding::Wrap)
    }

    pub fn create(window: &Window, root: &Node,
                  filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        HTMLCollection::new(window, root, filter)
    }

    fn validate_cache(&self) {
        // Clear the cache if the root version is different from our cached version
        let cached_version = self.cached_version.get();
        let curr_version = self.root.get_inclusive_descendants_version();
        if curr_version != cached_version {
            // Default values for the cache
            self.cached_version.set(curr_version);
            self.cached_cursor_element.set(None);
            self.cached_length.set(OptionU32::none());
            self.cached_cursor_index.set(OptionU32::none());
        }
    }

    fn set_cached_cursor(&self, index: u32, element: Option<Root<Element>>) -> Option<Root<Element>> {
        if let Some(element) = element {
            self.cached_cursor_index.set(OptionU32::some(index));
            self.cached_cursor_element.set(Some(element.r()));
            Some(element)
        } else {
            None
        }
    }

    pub fn by_tag_name(window: &Window, root: &Node, mut tag: DOMString)
                       -> Root<HTMLCollection> {
        let tag_atom = Atom::from(&*tag);
        tag.make_ascii_lowercase();
        let ascii_lower_tag = Atom::from(tag); // FIXME(ajeffrey): don't clone atom if it was already lowercased.
        HTMLCollection::by_atomic_tag_name(window, root, tag_atom, ascii_lower_tag)
    }

    pub fn by_atomic_tag_name(window: &Window, root: &Node, tag_atom: Atom, ascii_lower_tag: Atom)
                       -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameFilter {
            tag: Atom,
            ascii_lower_tag: Atom,
        }
        impl CollectionFilter for TagNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                if self.tag == atom!("*") {
                    true
                } else if elem.html_element_in_html_document() {
                    *elem.local_name() == self.ascii_lower_tag
                } else {
                    *elem.local_name() == self.tag
                }
            }
        }
        let filter = TagNameFilter {
            tag: tag_atom,
            ascii_lower_tag: ascii_lower_tag,
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString,
                          maybe_ns: Option<DOMString>) -> Root<HTMLCollection> {
        let local = Atom::from(tag);
        let ns = namespace_from_domstring(maybe_ns);
        let qname = QualName::new(ns, local);
        HTMLCollection::by_qual_tag_name(window, root, qname)
    }

    pub fn by_qual_tag_name(window: &Window, root: &Node, qname: QualName) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameNSFilter {
            qname: QualName
        }
        impl CollectionFilter for TagNameNSFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                    ((self.qname.ns == Namespace(atom!("*"))) || (self.qname.ns == *elem.namespace()))
                &&  ((self.qname.local == atom!("*")) || (self.qname.local == *elem.local_name()))
            }
        }
        let filter = TagNameNSFilter {
            qname: qname
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_class_name(window: &Window, root: &Node, classes: DOMString)
                         -> Root<HTMLCollection> {
        let class_atoms = split_html_space_chars(&classes).map(Atom::from).collect();
        HTMLCollection::by_atomic_class_name(window, root, class_atoms)
    }

    pub fn by_atomic_class_name(window: &Window, root: &Node, classes: Vec<Atom>)
                         -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ClassNameFilter {
            classes: Vec<Atom>
        }
        impl CollectionFilter for ClassNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                self.classes.iter().all(|class| elem.has_class(class))
            }
        }
        let filter = ClassNameFilter {
            classes: classes
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn children(window: &Window, root: &Node) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ElementChildFilter;
        impl CollectionFilter for ElementChildFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                root.is_parent_of(elem.upcast())
            }
        }
        HTMLCollection::create(window, root, box ElementChildFilter)
    }

    pub fn elements_iter_after(&self, after: &Node) -> HTMLCollectionElementsIter {
        // Iterate forwards from a node.
        HTMLCollectionElementsIter {
            node_iter: box after.following_nodes(&self.root),
            root: Root::from_ref(&self.root),
            filter: &self.filter,
        }
    }

    pub fn elements_iter(&self) -> HTMLCollectionElementsIter {
        // Iterate forwards from the root.
        self.elements_iter_after(&*self.root)
    }

    pub fn elements_iter_before(&self, before: &Node) -> HTMLCollectionElementsIter {
        // Iterate backwards from a node.
        HTMLCollectionElementsIter {
            node_iter: box before.preceding_nodes(&self.root),
            root: Root::from_ref(&self.root),
            filter: &self.filter,
        }
    }

}

// TODO: Make this generic, and avoid code duplication
pub struct HTMLCollectionElementsIter<'a> {
    node_iter: Box<Iterator<Item = Root<Node>>>,
    root: Root<Node>,
    filter: &'a Box<CollectionFilter>,
}

impl<'a> Iterator for HTMLCollectionElementsIter<'a> {
    type Item = Root<Element>;

    fn next(&mut self) -> Option<Self::Item> {
        let filter = &self.filter;
        let root = &self.root;
        self.node_iter.by_ref()
                      .filter_map(Root::downcast)
                      .filter(|element| filter.filter(&element, root))
                      .next()
   }
}

impl HTMLCollectionMethods for HTMLCollection {
    // https://dom.spec.whatwg.org/#dom-htmlcollection-length
    fn Length(&self) -> u32 {
        self.validate_cache();

        if let Some(cached_length) = self.cached_length.get().to_option() {
            // Cache hit
            cached_length
        } else {
            // Cache miss, calculate the length
            let length = self.elements_iter().count() as u32;
            self.cached_length.set(OptionU32::some(length));
            length
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn Item(&self, index: u32) -> Option<Root<Element>> {
        self.validate_cache();

        if let Some(element) = self.cached_cursor_element.get() {
            // Cache hit, the cursor element is set
            if let Some(cached_index) = self.cached_cursor_index.get().to_option() {
                if cached_index == index {
                    // The cursor is the element we're looking for
                    Some(element)
                } else if cached_index < index {
                    // The cursor is before the element we're looking for
                    // Iterate forwards, starting at the cursor.
                    let offset = index - (cached_index + 1);
                    let node: Root<Node> = Root::upcast(element);
                    self.set_cached_cursor(index, self.elements_iter_after(node.r()).nth(offset as usize))
                } else {
                    // The cursor is after the element we're looking for
                    // Iterate backwards, starting at the cursor.
                    let offset = cached_index - (index + 1);
                    let node: Root<Node> = Root::upcast(element);
                    self.set_cached_cursor(index, self.elements_iter_before(node.r()).nth(offset as usize))
                }
            } else {
                // Cache miss
                // Iterate forwards through all the nodes
                self.set_cached_cursor(index, self.elements_iter().nth(index as usize))
            }
        } else {
            // Cache miss
            // Iterate forwards through all the nodes
            self.set_cached_cursor(index, self.elements_iter().nth(index as usize))
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
    fn NamedItem(&self, key: DOMString) -> Option<Root<Element>> {
        // Step 1.
        if key.is_empty() {
            return None;
        }

        // Step 2.
        self.elements_iter().find(|elem| {
            elem.get_string_attribute(&atom!("id")) == key ||
            (elem.namespace() == &ns!(html) && elem.get_string_attribute(&atom!("name")) == key)
        })
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.Item(index);
        *found = maybe_elem.is_some();
        maybe_elem
    }

    // check-tidy: no specs after this line
    fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.NamedItem(name);
        *found = maybe_elem.is_some();
        maybe_elem
    }

    // https://dom.spec.whatwg.org/#interface-htmlcollection
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        // Step 1
        let mut result = vec![];

        // Step 2
        for elem in self.elements_iter() {
            // Step 2.1
            let id_attr = elem.get_string_attribute(&atom!("id"));
            if !id_attr.is_empty() && !result.contains(&id_attr) {
                result.push(id_attr)
            }
            // Step 2.2
            let name_attr = elem.get_string_attribute(&atom!("name"));
            if !name_attr.is_empty() && !result.contains(&name_attr) && *elem.namespace() == ns!(html) {
                result.push(name_attr)
            }
        }

        // Step 3
        result
    }
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/metadevs/video/mod.rs
///! Video (Display) management
use _common::*;
use sync::mutex::{Mutex,LazyMutex};
use lib::sparse_vec::SparseVec;

pub use self::geom::{Pos,Dims,Rect};

module_define!{Video, [], init}

/// Pre-driver graphics support (using a bootloader provided video mode)
pub mod bootvideo;

/// Geometry types (Pos, Dims, Rect)
mod geom;

/// Handle held by framebuffer drivers
pub struct FramebufferRegistration
{
	reg_id: usize,
}

/**
 * "Device"-side display surface
 *
 * A single framebuffer can be bound to multiple outputs (as a mirror).
 * Multiple separate outputs are handled with multiple framebuffers
 */
pub trait Framebuffer: 'static + Send
{
	fn as_any(&self) -> &Any;
	fn activate(&mut self);

	fn get_size(&self) -> Dims;
	fn set_size(&mut self, newsize: Dims) -> bool;

	fn blit_inner(&mut self, dst: Rect, src: Rect);
	fn blit_ext(&mut self, dst: Rect, src: Rect, srf: &Framebuffer) -> bool;
	fn blit_buf(&mut self, dst: Rect, buf: &[u32]);
	fn fill(&mut self, dst: Rect, colour: u32);

	// TODO: Handle 3D units
}

pub struct DisplaySurface
{
	region: Rect,
	fb: Box<Framebuffer>,
}
//struct HiddenSurface
//{
//	fb: Box<Framebuffer>,
//}

/// Sparse list of registered display devices
static S_DISPLAY_SURFACES: LazyMutex<SparseVec<DisplaySurface>> = lazymutex_init!( );
/// Boot video mode
static mut S_BOOT_MODE: Option<bootvideo::VideoMode> = None;
static S_GEOM_UPDATE_SIGNAL: Mutex<Option<fn(new_total: Rect)>> = mutex_init!(None);

fn init()
{
	S_DISPLAY_SURFACES.init( || SparseVec::new() );

	if let Some(mode) = unsafe { S_BOOT_MODE.as_ref() }
	{
		log_notice!("Using boot video mode {:?}", mode);
		let fb = box bootvideo::Framebuffer::new(*mode) as Box<Framebuffer>;
		let dims = fb.get_size();
		S_DISPLAY_SURFACES.lock().insert( DisplaySurface {
			region: Rect::new(0,0, dims.w,dims.h),
			fb: fb
			} );
	}
	else
	{
		log_warning!("No boot video mode set");
	}
}

/// Set the boot video mode.
///
/// NOTE: Must be called before this module is initialised to have any effect
pub fn set_boot_mode(mode: bootvideo::VideoMode)
{
	unsafe {
		assert!(S_BOOT_MODE.is_none(), "Boot video mode set multiple times");
		S_BOOT_MODE = Some(mode);
	}
}

pub fn register_geom_update(fcn: fn(new_total: Rect))
{
	let mut lh = S_GEOM_UPDATE_SIGNAL.lock();
	assert!(lh.is_some(), "register_geom_update called multiple times");
	*lh = Some(fcn);
}

/// Add an output to the display manager
pub fn add_output(output: Box<Framebuffer>) -> FramebufferRegistration
{
	let dims = output.get_size();
	// Detect if the boot mode is still present, and clear if it is
	unsafe {
		if S_BOOT_MODE.take().is_some()
		{
			// - Create a registration for #0, aka the boot mode, and then drop it
			::core::mem::drop( FramebufferRegistration { reg_id: 0 } );
			log_notice!("Alternative display driver loaded, dropping boot video");
		}
	}

	// Add new output to the global list
	let mut lh = S_DISPLAY_SURFACES.lock();
	let pos = if lh.count() == 0 {
			Pos::new(0, 0)
		} else {
			todo!("add_output: Pick a suitable location for the new surface");
		};
	let idx = lh.insert( DisplaySurface {
		region: Rect::new(pos.x,pos.y,dims.w,dims.h),
		fb: output
		} );

	// API Requirements
	// - New surface added (with location)
	// - Old surface removed
	// - Surface relocated (as part of removal/sorting/editing)
	// > Could just have a generic "things changed" call and let the GUI/user poll request the new state
	if let Some(fcn) = *S_GEOM_UPDATE_SIGNAL.lock()
	{
		let total_area = lh.iter().map(|x| x.region).fold(Rect::new(0,0,0,0), |a,b| a.union(&b));
		fcn( total_area );
	}

	log_debug!("Registering framebuffer #{}", idx);
	FramebufferRegistration {
		reg_id: idx
	}
}

/// Returns the display region that contains the given point
pub fn get_display_for_pos(pos: Pos) -> Option<Rect>
{
	for surf in S_DISPLAY_SURFACES.lock().iter_mut()
	{
		if surf.region.contains(&pos)
		{
			return Some(surf.region);
		}
	}
	None
}

/// Write part of a single scanline to the screen
///
/// Unsafe because it (eventually) will be able to cause multiple writers
pub unsafe fn write_line(pos: Pos, data: &[u32])
{
	let rect = Rect { pos: pos, dims: Dims::new(data.len() as u32, 1) };
	// 1. Locate surface
	for surf in S_DISPLAY_SURFACES.lock().iter_mut()
	{
		if let Some(sub_rect) = surf.region.intersect(&rect)
		{
			let ofs_l = sub_rect.left() - rect.left();
			let ofs_r = sub_rect.right() - rect.left();
			// 2. Blit to it
			surf.fb.blit_buf(sub_rect, &data[ofs_l as usize .. ofs_r as usize]);
		}
	}
}

impl ::core::ops::Drop for FramebufferRegistration
{
	fn drop(&mut self)
	{
		S_DISPLAY_SURFACES.lock().remove(self.reg_id);
	}
}

// vim: ft=rust
<commit_msg>Video - Fix silly bug with incorrect condition in assert!()<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/metadevs/video/mod.rs
///! Video (Display) management
use _common::*;
use sync::mutex::{Mutex,LazyMutex};
use lib::sparse_vec::SparseVec;

pub use self::geom::{Pos,Dims,Rect};

module_define!{Video, [], init}

/// Pre-driver graphics support (using a bootloader provided video mode)
pub mod bootvideo;

/// Geometry types (Pos, Dims, Rect)
mod geom;

/// Handle held by framebuffer drivers
pub struct FramebufferRegistration
{
	reg_id: usize,
}

/**
 * "Device"-side display surface
 *
 * A single framebuffer can be bound to multiple outputs (as a mirror).
 * Multiple separate outputs are handled with multiple framebuffers
 */
pub trait Framebuffer: 'static + Send
{
	fn as_any(&self) -> &Any;
	fn activate(&mut self);

	fn get_size(&self) -> Dims;
	fn set_size(&mut self, newsize: Dims) -> bool;

	fn blit_inner(&mut self, dst: Rect, src: Rect);
	fn blit_ext(&mut self, dst: Rect, src: Rect, srf: &Framebuffer) -> bool;
	fn blit_buf(&mut self, dst: Rect, buf: &[u32]);
	fn fill(&mut self, dst: Rect, colour: u32);

	// TODO: Handle 3D units
}

pub struct DisplaySurface
{
	region: Rect,
	fb: Box<Framebuffer>,
}
//struct HiddenSurface
//{
//	fb: Box<Framebuffer>,
//}

/// Sparse list of registered display devices
static S_DISPLAY_SURFACES: LazyMutex<SparseVec<DisplaySurface>> = lazymutex_init!( );
/// Boot video mode
static mut S_BOOT_MODE: Option<bootvideo::VideoMode> = None;
static S_GEOM_UPDATE_SIGNAL: Mutex<Option<fn(new_total: Rect)>> = mutex_init!(None);

fn init()
{
	S_DISPLAY_SURFACES.init( || SparseVec::new() );

	if let Some(mode) = unsafe { S_BOOT_MODE.as_ref() }
	{
		log_notice!("Using boot video mode {:?}", mode);
		let fb = box bootvideo::Framebuffer::new(*mode) as Box<Framebuffer>;
		let dims = fb.get_size();
		S_DISPLAY_SURFACES.lock().insert( DisplaySurface {
			region: Rect::new(0,0, dims.w,dims.h),
			fb: fb
			} );
	}
	else
	{
		log_warning!("No boot video mode set");
	}
}

/// Set the boot video mode.
///
/// NOTE: Must be called before this module is initialised to have any effect
pub fn set_boot_mode(mode: bootvideo::VideoMode)
{
	unsafe {
		assert!(S_BOOT_MODE.is_none(), "Boot video mode set multiple times");
		S_BOOT_MODE = Some(mode);
	}
}

pub fn register_geom_update(fcn: fn(new_total: Rect))
{
	let mut lh = S_GEOM_UPDATE_SIGNAL.lock();
	assert!(lh.is_none(), "register_geom_update called multiple times (prev {:p}, new {:p})", lh.as_ref().unwrap(), &fcn);
	*lh = Some(fcn);
}

/// Add an output to the display manager
pub fn add_output(output: Box<Framebuffer>) -> FramebufferRegistration
{
	let dims = output.get_size();
	// Detect if the boot mode is still present, and clear if it is
	unsafe {
		if S_BOOT_MODE.take().is_some()
		{
			// - Create a registration for #0, aka the boot mode, and then drop it
			::core::mem::drop( FramebufferRegistration { reg_id: 0 } );
			log_notice!("Alternative display driver loaded, dropping boot video");
		}
	}

	// Add new output to the global list
	let mut lh = S_DISPLAY_SURFACES.lock();
	let pos = if lh.count() == 0 {
			Pos::new(0, 0)
		} else {
			todo!("add_output: Pick a suitable location for the new surface");
		};
	let idx = lh.insert( DisplaySurface {
		region: Rect::new(pos.x,pos.y,dims.w,dims.h),
		fb: output
		} );

	// API Requirements
	// - New surface added (with location)
	// - Old surface removed
	// - Surface relocated (as part of removal/sorting/editing)
	// > Could just have a generic "things changed" call and let the GUI/user poll request the new state
	if let Some(fcn) = *S_GEOM_UPDATE_SIGNAL.lock()
	{
		let total_area = lh.iter().map(|x| x.region).fold(Rect::new(0,0,0,0), |a,b| a.union(&b));
		fcn( total_area );
	}

	log_debug!("Registering framebuffer #{}", idx);
	FramebufferRegistration {
		reg_id: idx
	}
}

/// Returns the display region that contains the given point
pub fn get_display_for_pos(pos: Pos) -> Option<Rect>
{
	for surf in S_DISPLAY_SURFACES.lock().iter_mut()
	{
		if surf.region.contains(&pos)
		{
			return Some(surf.region);
		}
	}
	None
}

/// Write part of a single scanline to the screen
///
/// Unsafe because it (eventually) will be able to cause multiple writers
pub unsafe fn write_line(pos: Pos, data: &[u32])
{
	let rect = Rect { pos: pos, dims: Dims::new(data.len() as u32, 1) };
	// 1. Locate surface
	for surf in S_DISPLAY_SURFACES.lock().iter_mut()
	{
		if let Some(sub_rect) = surf.region.intersect(&rect)
		{
			let ofs_l = sub_rect.left() - rect.left();
			let ofs_r = sub_rect.right() - rect.left();
			// 2. Blit to it
			surf.fb.blit_buf(sub_rect, &data[ofs_l as usize .. ofs_r as usize]);
		}
	}
}

impl ::core::ops::Drop for FramebufferRegistration
{
	fn drop(&mut self)
	{
		S_DISPLAY_SURFACES.lock().remove(self.reg_id);
	}
}

// vim: ft=rust
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

macro_rules! borrow {
    ($x:expr) => { &$x } //~ ERROR mismatched types
}

fn foo(_: String) {}

fn foo2(s: &String) {
    foo(s);
    //~^ ERROR mismatched types
}

fn foo3(_: u32) {}
fn foo4(u: &u32) {
    foo3(u);
    //~^ ERROR mismatched types
}

fn main() {
    let s = String::new();
    let r_s = &s;
    foo2(r_s);
    foo(&"aaa".to_owned());
    //~^ ERROR mismatched types
    foo(&mut "aaa".to_owned());
    //~^ ERROR mismatched types
    foo3(borrow!(0));
    foo4(&0);
    assert_eq!(3i32, &3i32);
    //~^ ERROR mismatched types
}<commit_msg>readd final newline<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

macro_rules! borrow {
    ($x:expr) => { &$x } //~ ERROR mismatched types
}

fn foo(_: String) {}

fn foo2(s: &String) {
    foo(s);
    //~^ ERROR mismatched types
}

fn foo3(_: u32) {}
fn foo4(u: &u32) {
    foo3(u);
    //~^ ERROR mismatched types
}

fn main() {
    let s = String::new();
    let r_s = &s;
    foo2(r_s);
    foo(&"aaa".to_owned());
    //~^ ERROR mismatched types
    foo(&mut "aaa".to_owned());
    //~^ ERROR mismatched types
    foo3(borrow!(0));
    foo4(&0);
    assert_eq!(3i32, &3i32);
    //~^ ERROR mismatched types
}
<|endoftext|>


<commit_before>use env::Env;
use library;

#[derive(Debug)]
pub struct Trampoline {
    pub name: String,
    pub parameters: Vec<library::Parameter>,
    pub ret: library::Parameter,
}

pub type Trampolines = Vec<Trampoline>;

pub fn analyze(env: &Env, signal: &library::Signal, trampolines: &mut Trampolines) -> Option<String> {
    let name = generate_name(env, signal, trampolines);
    if name.is_none() { return None }

    let trampoline = Trampoline {
        name: name.clone().unwrap(),
        parameters: signal.parameters.clone(),
        ret: signal.ret.clone(),
    };
    trampolines.push(trampoline);
    name
}

fn generate_name(env: &Env, signal: &library::Signal, trampolines: &mut Trampolines) -> Option<String> {
    let ret_name = match name_from_return_type(env, &signal.ret) {
        Some(name) => name,
        None => return None,
    };
    let mut name = if signal.parameters.is_empty() {
        Some(format!("{}_trampoline", ret_name))
    } else {
        //TODO: trampolines with params
        None
    };

    if name.is_none() {
        name = Some(format!("{}_unnamed_trampoline_{}", ret_name, trampolines.len() + 1));
    }
    name
}

fn name_from_return_type(env: &Env, ret: &library::Parameter) -> Option<String> {
    use library::Type::*;
    let some = |s: &str| Some(s.to_owned());
    match *env.type_(ret.typ) {
        Fundamental(fund) => {
            use library::Fundamental;
            match fund {
                Fundamental::None => some("void"),
                Fundamental::Boolean => some("bool"),
                Fundamental::Int => some("int"),
                _ => None,
            }
        }
        _ => None
    }
    /* TODO: other return types:
    ComboBox:format-entry-text => return utf8
    confirm-overwrite => return GtkFileChooserConfirmation
    create-context => return Gdk.GLContext
    create-window => return Notebook
    create-custom-widget => return GObject.Object
    Scale:format-value => return utf8
    */
}
<commit_msg>Name trampolines after signal<commit_after>use env::Env;
use library;
use nameutil::signal_to_snake;
use super::conversion_type::ConversionType;

#[derive(Debug)]
pub struct Trampoline {
    pub name: String,
    pub parameters: Vec<library::Parameter>,
    pub ret: library::Parameter,
}

pub type Trampolines = Vec<Trampoline>;

pub fn analyze(env: &Env, signal: &library::Signal, trampolines: &mut Trampolines) -> Option<String> {
    if !can_generate(env, signal) {
        return None;
    }

    let name = format!("{}_trampoline", signal_to_snake(&signal.name));

    let trampoline = Trampoline {
        name: name.clone(),
        parameters: signal.parameters.clone(),
        ret: signal.ret.clone(),
    };
    trampolines.push(trampoline);
    Some(name)
}

fn can_generate(env: &Env, signal: &library::Signal) -> bool {
    if signal.ret.typ != Default::default() &&
        ConversionType::of(&env.library, signal.ret.typ) == ConversionType::Unknown {
            return false;
        }
    for par in &signal.parameters {
        if ConversionType::of(&env.library, par.typ) == ConversionType::Unknown {
            return false;
        }
    }
    true
}
<|endoftext|>


<commit_before>use std::ffi::OsString;
use std::path::Path;

use core::Source;
use sources::PathSource;
use ops::{self, ExecEngine, ProcessEngine, Compilation};
use util::{CargoResult, ProcessError};

pub struct TestOptions<'a, 'b: 'a> {
    pub compile_opts: ops::CompileOptions<'a, 'b>,
    pub no_run: bool,
}

pub fn run_tests(manifest_path: &Path,
                 options: &TestOptions,
                 test_args: &[String]) -> CargoResult<Option<ProcessError>> {
    let config = options.compile_opts.config;
    let compile = match try!(build_and_run(manifest_path, options, test_args)) {
        Ok(compile) => compile,
        Err(e) => return Ok(Some(e)),
    };

    let libs = compile.package.targets().iter().filter_map(|target| {
        if !target.doctested() || !target.is_lib() {
            return None
        }
        Some((target.src_path(), target.name()))
    });

    for (lib, name) in libs {
        try!(config.shell().status("Doc-tests", name));
        let mut p = try!(compile.rustdoc_process(&compile.package));
        p.arg("--test").arg(lib)
         .arg("--crate-name").arg(name)
         .arg("-L").arg(&compile.root_output)
         .arg("-L").arg(&compile.deps_output)
         .cwd(compile.package.root());

        if test_args.len() > 0 {
            p.arg("--test-args").arg(&test_args.connect(" "));
        }

        for feat in compile.features.iter() {
            p.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
        }

        for (pkg, libs) in compile.libraries.iter() {
            for lib in libs.iter() {
                let mut arg = OsString::from_str(pkg.name());
                arg.push("=");
                arg.push(lib);
                p.arg("--extern").arg(&arg);
            }
        }

        try!(config.shell().verbose(|shell| {
            shell.status("Running", p.to_string())
        }));
        match ExecEngine::exec(&mut ProcessEngine, p) {
            Ok(()) => {}
            Err(e) => return Ok(Some(e)),
        }
    }

    Ok(None)
}

pub fn run_benches(manifest_path: &Path,
                   options: &TestOptions,
                   args: &[String]) -> CargoResult<Option<ProcessError>> {
    let mut args = args.to_vec();
    args.push("--bench".to_string());

    Ok(try!(build_and_run(manifest_path, options, &args)).err())
}

fn build_and_run(manifest_path: &Path,
                 options: &TestOptions,
                 test_args: &[String])
                 -> CargoResult<Result<Compilation, ProcessError>> {
    let config = options.compile_opts.config;
    let mut source = try!(PathSource::for_path(&manifest_path.parent().unwrap(),
                                               config));
    try!(source.update());

    let mut compile = try!(ops::compile(manifest_path, &options.compile_opts));
    if options.no_run { return Ok(Ok(compile)) }
    compile.tests.sort();

    let cwd = config.cwd();
    for &(_, ref exe) in &compile.tests {
        let to_display = match exe.relative_from(&cwd) {
            Some(path) => path,
            None => &**exe,
        };
        let mut cmd = try!(compile.target_process(exe, &compile.package));
        cmd.args(test_args);
        try!(config.shell().concise(|shell| {
            shell.status("Running", to_display.display().to_string())
        }));
        try!(config.shell().verbose(|shell| {
            shell.status("Running", cmd.to_string())
        }));
        match ExecEngine::exec(&mut ProcessEngine, cmd) {
            Ok(()) => {}
            Err(e) => return Ok(Err(e))
        }
    }

    Ok(Ok(compile))
}
<commit_msg>Fix cargo test --no-run<commit_after>use std::ffi::OsString;
use std::path::Path;

use core::Source;
use sources::PathSource;
use ops::{self, ExecEngine, ProcessEngine, Compilation};
use util::{CargoResult, ProcessError};

pub struct TestOptions<'a, 'b: 'a> {
    pub compile_opts: ops::CompileOptions<'a, 'b>,
    pub no_run: bool,
}

pub fn run_tests(manifest_path: &Path,
                 options: &TestOptions,
                 test_args: &[String]) -> CargoResult<Option<ProcessError>> {
    let config = options.compile_opts.config;
    let compile = match try!(build_and_run(manifest_path, options, test_args)) {
        Ok(compile) => compile,
        Err(e) => return Ok(Some(e)),
    };

    if options.no_run { return Ok(None) }

    let libs = compile.package.targets().iter()
                      .filter(|t| t.doctested())
                      .map(|t| (t.src_path(), t.name()));

    for (lib, name) in libs {
        try!(config.shell().status("Doc-tests", name));
        let mut p = try!(compile.rustdoc_process(&compile.package));
        p.arg("--test").arg(lib)
         .arg("--crate-name").arg(name)
         .arg("-L").arg(&compile.root_output)
         .arg("-L").arg(&compile.deps_output)
         .cwd(compile.package.root());

        if test_args.len() > 0 {
            p.arg("--test-args").arg(&test_args.connect(" "));
        }

        for feat in compile.features.iter() {
            p.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
        }

        for (pkg, libs) in compile.libraries.iter() {
            for lib in libs.iter() {
                let mut arg = OsString::from_str(pkg.name());
                arg.push("=");
                arg.push(lib);
                p.arg("--extern").arg(&arg);
            }
        }

        try!(config.shell().verbose(|shell| {
            shell.status("Running", p.to_string())
        }));
        match ExecEngine::exec(&mut ProcessEngine, p) {
            Ok(()) => {}
            Err(e) => return Ok(Some(e)),
        }
    }

    Ok(None)
}

pub fn run_benches(manifest_path: &Path,
                   options: &TestOptions,
                   args: &[String]) -> CargoResult<Option<ProcessError>> {
    let mut args = args.to_vec();
    args.push("--bench".to_string());

    Ok(try!(build_and_run(manifest_path, options, &args)).err())
}

fn build_and_run(manifest_path: &Path,
                 options: &TestOptions,
                 test_args: &[String])
                 -> CargoResult<Result<Compilation, ProcessError>> {
    let config = options.compile_opts.config;
    let mut source = try!(PathSource::for_path(&manifest_path.parent().unwrap(),
                                               config));
    try!(source.update());

    let mut compile = try!(ops::compile(manifest_path, &options.compile_opts));
    if options.no_run { return Ok(Ok(compile)) }
    compile.tests.sort();

    let cwd = config.cwd();
    for &(_, ref exe) in &compile.tests {
        let to_display = match exe.relative_from(&cwd) {
            Some(path) => path,
            None => &**exe,
        };
        let mut cmd = try!(compile.target_process(exe, &compile.package));
        cmd.args(test_args);
        try!(config.shell().concise(|shell| {
            shell.status("Running", to_display.display().to_string())
        }));
        try!(config.shell().verbose(|shell| {
            shell.status("Running", cmd.to_string())
        }));
        match ExecEngine::exec(&mut ProcessEngine, cmd) {
            Ok(()) => {}
            Err(e) => return Ok(Err(e))
        }
    }

    Ok(Ok(compile))
}
<|endoftext|>


<commit_before>extern crate inkwell;

use self::inkwell::attributes::Attribute;
use self::inkwell::context::Context;

use std::ffi::CString;

#[test]
fn test_enum_attribute_kinds() {
    // Does not exist:
    assert_eq!(Attribute::get_named_enum_kind_id("foo"), 0);
    assert_eq!(Attribute::get_named_enum_kind_id("bar"), 0);

    // Many of the values change and are not consistent across LLVM versions
    // so it only seems to make sense to test some consistent subset.
    // Otherwise it's not worth keeping track of. Users will have to
    // play around with it and determine which ones they are interested in
    // for their particular LLVM version

    // Function Attributes:
    assert_eq!(Attribute::get_named_enum_kind_id("allocsize"), 2);
    assert_eq!(Attribute::get_named_enum_kind_id("alwaysinline"), 3);
    assert_eq!(Attribute::get_named_enum_kind_id("argmemonly"), 4);
    assert_eq!(Attribute::get_named_enum_kind_id("builtin"), 5);
    assert_eq!(Attribute::get_named_enum_kind_id("cold"), 7);
    assert_eq!(Attribute::get_named_enum_kind_id("convergent"), 8);
    assert_eq!(Attribute::get_named_enum_kind_id("inaccessiblememonly"), 13);
    assert_eq!(Attribute::get_named_enum_kind_id("inaccessiblemem_or_argmemonly"), 14);
    assert_eq!(Attribute::get_named_enum_kind_id("inlinehint"), 15);
    assert_eq!(Attribute::get_named_enum_kind_id("jumptable"), 16);
    assert_eq!(Attribute::get_named_enum_kind_id("minsize"), 17);
    assert_eq!(Attribute::get_named_enum_kind_id("naked"), 18);
    assert_eq!(Attribute::get_named_enum_kind_id("nobuiltin"), 21);

    // REVIEW: The LLVM docs suggest these fn attrs exist, but don't turn up:
    // assert_eq!(Attribute::get_named_enum_kind_id("no-jump-tables"), 19);
    // assert_eq!(Attribute::get_named_enum_kind_id("null-pointer-is-valid"), 31);
    // assert_eq!(Attribute::get_named_enum_kind_id("optforfuzzing"), 32);
    // assert_eq!(Attribute::get_named_enum_kind_id("patchable-function"), 35);
    // assert_eq!(Attribute::get_named_enum_kind_id("probe-stack"), 36);
    // assert_eq!(Attribute::get_named_enum_kind_id("stack-probe-size"), 36);
    // assert_eq!(Attribute::get_named_enum_kind_id("no-stack-arg-probe"), 32);
    // assert_eq!(Attribute::get_named_enum_kind_id("thunk"), 45);
    // assert_eq!(Attribute::get_named_enum_kind_id("nocf_check"), 45);
    // assert_eq!(Attribute::get_named_enum_kind_id("shadowcallstack"), 45);

    // Parameter Attributes:
    assert_eq!(Attribute::get_named_enum_kind_id("align"), 1);
    assert_eq!(Attribute::get_named_enum_kind_id("byval"), 6);
    assert_eq!(Attribute::get_named_enum_kind_id("dereferenceable"), 9);
    assert_eq!(Attribute::get_named_enum_kind_id("dereferenceable_or_null"), 10);
    assert_eq!(Attribute::get_named_enum_kind_id("inalloca"), 11);
    assert_eq!(Attribute::get_named_enum_kind_id("inreg"), 12);
    assert_eq!(Attribute::get_named_enum_kind_id("nest"), 19);
    assert_eq!(Attribute::get_named_enum_kind_id("noalias"), 20);
    assert_eq!(Attribute::get_named_enum_kind_id("nocapture"), 22);
}

#[test]
fn test_enum_attributes() {
    let context = Context::create();
    let enum_attribute = context.create_enum_attribute(0, 10);

    assert!(enum_attribute.is_enum());
    assert!(!enum_attribute.is_string());

    assert_eq!(enum_attribute.get_enum_kind_id(), 0);
    assert_eq!(enum_attribute.get_enum_value(), 10);

    let enum_attribute2 = context.create_enum_attribute(3, 14);

    assert!(enum_attribute2.is_enum());
    assert!(!enum_attribute2.is_string());

    assert_eq!(enum_attribute2.get_enum_kind_id(), 3);
    assert_eq!(enum_attribute2.get_enum_value(), 14);

    let enum_attribute3 = context.create_enum_attribute(56, 20);

    assert!(enum_attribute3.is_enum());
    assert!(!enum_attribute3.is_string());

    assert_eq!(enum_attribute3.get_enum_kind_id(), 56);
    assert_eq!(enum_attribute3.get_enum_value(), 20);
}

#[test]
fn test_string_attributes() {
    let context = Context::create();
    let string_attribute = context.create_string_attribute("my_key_123", "my_val");

    assert!(!string_attribute.is_enum());
    assert!(string_attribute.is_string());

    assert_eq!(*string_attribute.get_string_kind_id(), *CString::new("my_key_123").unwrap());
    assert_eq!(*string_attribute.get_string_value(), *CString::new("my_val").unwrap());
}

#[test]
fn test_attributes_on_function_values() {
    let context = Context::create();
    let builder = context.create_builder();
    let module = context.create_module("my_mod");
    let void_type = context.void_type();
    let i32_type = context.i32_type();
    let fn_type = void_type.fn_type(&[i32_type.into()], false);
    let fn_value = module.add_function("my_fn", fn_type, None);
    let entry_bb = fn_value.append_basic_block("entry");
    let string_attribute = context.create_string_attribute("my_key", "my_val");
    let alignstack_attribute = Attribute::get_named_enum_kind_id("alignstack");
    let enum_attribute = context.create_enum_attribute(alignstack_attribute, 1);

    builder.position_at_end(&entry_bb);
    builder.build_return(None);

    assert_eq!(fn_value.count_attributes(0), 0);
    assert_eq!(fn_value.count_attributes(1), 0);

    fn_value.remove_string_attribute(0, "my_key"); // Noop
    fn_value.remove_enum_attribute(0, alignstack_attribute); // Noop

    // define align 1 "my_key"="my_val" void @my_fn()
    fn_value.add_attribute(0, string_attribute);
    fn_value.add_attribute(1, string_attribute); // Applied to 1st param
    fn_value.add_attribute(0, enum_attribute);

    assert_eq!(fn_value.count_attributes(0), 2);
    assert_eq!(fn_value.get_enum_attribute(0, alignstack_attribute), Some(enum_attribute));
    assert_eq!(fn_value.get_string_attribute(0, "my_key"), Some(string_attribute));

    fn_value.remove_string_attribute(0, "my_key");

    assert_eq!(fn_value.count_attributes(0), 1);

    fn_value.remove_enum_attribute(0, alignstack_attribute);

    assert_eq!(fn_value.count_attributes(0), 0);
    assert!(fn_value.get_enum_attribute(0, alignstack_attribute).is_none());
    assert!(fn_value.get_string_attribute(0, "my_key").is_none());
}

#[test]
fn test_attributes_on_call_site_values() {
    let context = Context::create();
    let builder = context.create_builder();
    let module = context.create_module("my_mod");
    let void_type = context.void_type();
    let i32_type = context.i32_type();
    let fn_type = void_type.fn_type(&[i32_type.into()], false);
    let fn_value = module.add_function("my_fn", fn_type, None);
    let entry_bb = fn_value.append_basic_block("entry");
    let string_attribute = context.create_string_attribute("my_key", "my_val");
    let alignstack_attribute = Attribute::get_named_enum_kind_id("alignstack");
    let enum_attribute = context.create_enum_attribute(alignstack_attribute, 1);

    builder.position_at_end(&entry_bb);

    let call_site_value = builder.build_call(fn_value, &[], "my_fn");

    builder.build_return(None);

    assert_eq!(call_site_value.count_arguments(), 0);
    assert_eq!(call_site_value.count_attributes(0), 0);
    assert_eq!(call_site_value.count_attributes(1), 0);

    call_site_value.remove_string_attribute(0, "my_key"); // Noop
    call_site_value.remove_enum_attribute(0, alignstack_attribute); // Noop

    // define align 1 "my_key"="my_val" void @my_fn()
    call_site_value.add_attribute(0, string_attribute);
    call_site_value.add_attribute(1, string_attribute); // Applied to 1st param
    call_site_value.add_attribute(0, enum_attribute);

    assert_eq!(call_site_value.count_attributes(0), 2);
    assert_eq!(call_site_value.get_enum_attribute(0, alignstack_attribute), Some(enum_attribute));
    assert_eq!(call_site_value.get_string_attribute(0, "my_key"), Some(string_attribute));

    call_site_value.remove_string_attribute(0, "my_key");

    assert_eq!(call_site_value.count_attributes(0), 1);

    call_site_value.remove_enum_attribute(0, alignstack_attribute);

    assert_eq!(call_site_value.count_attributes(0), 0);
    assert!(call_site_value.get_enum_attribute(0, alignstack_attribute).is_none());
    assert!(call_site_value.get_string_attribute(0, "my_key").is_none());
    assert_eq!(call_site_value.get_called_fn_value(), fn_value);

    call_site_value.set_param_alignment_attribute(0, 16);

    assert_eq!(call_site_value.count_attributes(0), 1);
    assert!(call_site_value.get_enum_attribute(0, 1).is_some());
}
<commit_msg>Fix test_attributes.<commit_after>extern crate inkwell;

use self::inkwell::attributes::Attribute;
use self::inkwell::context::Context;

use std::ffi::CString;

#[test]
fn test_enum_attribute_kinds() {
    // Does not exist:
    assert_eq!(Attribute::get_named_enum_kind_id("foo"), 0);
    assert_eq!(Attribute::get_named_enum_kind_id("bar"), 0);

    // Many of the values change and are not consistent across LLVM versions
    // so it only seems to make sense to test some consistent subset.
    // Otherwise it's not worth keeping track of. Users will have to
    // play around with it and determine which ones they are interested in
    // for their particular LLVM version

    // Function Attributes:
    assert_eq!(Attribute::get_named_enum_kind_id("allocsize"), 2);
    assert_eq!(Attribute::get_named_enum_kind_id("alwaysinline"), 3);
    assert_eq!(Attribute::get_named_enum_kind_id("argmemonly"), 4);
    assert_eq!(Attribute::get_named_enum_kind_id("builtin"), 5);
    assert_eq!(Attribute::get_named_enum_kind_id("cold"), 7);
    assert_eq!(Attribute::get_named_enum_kind_id("convergent"), 8);
    assert_eq!(Attribute::get_named_enum_kind_id("inaccessiblememonly"), 13);
    assert_eq!(Attribute::get_named_enum_kind_id("inaccessiblemem_or_argmemonly"), 14);
    assert_eq!(Attribute::get_named_enum_kind_id("inlinehint"), 15);
    assert_eq!(Attribute::get_named_enum_kind_id("jumptable"), 16);
    assert_eq!(Attribute::get_named_enum_kind_id("minsize"), 17);
    assert_eq!(Attribute::get_named_enum_kind_id("naked"), 18);
    assert_eq!(Attribute::get_named_enum_kind_id("nobuiltin"), 21);

    // REVIEW: The LLVM docs suggest these fn attrs exist, but don't turn up:
    // assert_eq!(Attribute::get_named_enum_kind_id("no-jump-tables"), 19);
    // assert_eq!(Attribute::get_named_enum_kind_id("null-pointer-is-valid"), 31);
    // assert_eq!(Attribute::get_named_enum_kind_id("optforfuzzing"), 32);
    // assert_eq!(Attribute::get_named_enum_kind_id("patchable-function"), 35);
    // assert_eq!(Attribute::get_named_enum_kind_id("probe-stack"), 36);
    // assert_eq!(Attribute::get_named_enum_kind_id("stack-probe-size"), 36);
    // assert_eq!(Attribute::get_named_enum_kind_id("no-stack-arg-probe"), 32);
    // assert_eq!(Attribute::get_named_enum_kind_id("thunk"), 45);
    // assert_eq!(Attribute::get_named_enum_kind_id("nocf_check"), 45);
    // assert_eq!(Attribute::get_named_enum_kind_id("shadowcallstack"), 45);

    // Parameter Attributes:
    assert_eq!(Attribute::get_named_enum_kind_id("align"), 1);
    assert_eq!(Attribute::get_named_enum_kind_id("byval"), 6);
    assert_eq!(Attribute::get_named_enum_kind_id("dereferenceable"), 9);
    assert_eq!(Attribute::get_named_enum_kind_id("dereferenceable_or_null"), 10);
    assert_eq!(Attribute::get_named_enum_kind_id("inalloca"), 11);
    assert_eq!(Attribute::get_named_enum_kind_id("inreg"), 12);
    assert_eq!(Attribute::get_named_enum_kind_id("nest"), 19);
    assert_eq!(Attribute::get_named_enum_kind_id("noalias"), 20);
    assert_eq!(Attribute::get_named_enum_kind_id("nocapture"), 22);
}

#[test]
fn test_string_attributes() {
    let context = Context::create();
    let string_attribute = context.create_string_attribute("my_key_123", "my_val");

    assert!(!string_attribute.is_enum());
    assert!(string_attribute.is_string());

    assert_eq!(*string_attribute.get_string_kind_id(), *CString::new("my_key_123").unwrap());
    assert_eq!(*string_attribute.get_string_value(), *CString::new("my_val").unwrap());
}

#[test]
fn test_attributes_on_function_values() {
    let context = Context::create();
    let builder = context.create_builder();
    let module = context.create_module("my_mod");
    let void_type = context.void_type();
    let i32_type = context.i32_type();
    let fn_type = void_type.fn_type(&[i32_type.into()], false);
    let fn_value = module.add_function("my_fn", fn_type, None);
    let entry_bb = fn_value.append_basic_block("entry");
    let string_attribute = context.create_string_attribute("my_key", "my_val");
    let alignstack_attribute = Attribute::get_named_enum_kind_id("alignstack");
    let enum_attribute = context.create_enum_attribute(alignstack_attribute, 1);

    builder.position_at_end(&entry_bb);
    builder.build_return(None);

    assert_eq!(fn_value.count_attributes(0), 0);
    assert_eq!(fn_value.count_attributes(1), 0);

    fn_value.remove_string_attribute(0, "my_key"); // Noop
    fn_value.remove_enum_attribute(0, alignstack_attribute); // Noop

    // define align 1 "my_key"="my_val" void @my_fn()
    fn_value.add_attribute(0, string_attribute);
    fn_value.add_attribute(1, string_attribute); // Applied to 1st param
    fn_value.add_attribute(0, enum_attribute);

    assert_eq!(fn_value.count_attributes(0), 2);
    assert_eq!(fn_value.get_enum_attribute(0, alignstack_attribute), Some(enum_attribute));
    assert_eq!(fn_value.get_string_attribute(0, "my_key"), Some(string_attribute));

    fn_value.remove_string_attribute(0, "my_key");

    assert_eq!(fn_value.count_attributes(0), 1);

    fn_value.remove_enum_attribute(0, alignstack_attribute);

    assert_eq!(fn_value.count_attributes(0), 0);
    assert!(fn_value.get_enum_attribute(0, alignstack_attribute).is_none());
    assert!(fn_value.get_string_attribute(0, "my_key").is_none());
}

#[test]
fn test_attributes_on_call_site_values() {
    let context = Context::create();
    let builder = context.create_builder();
    let module = context.create_module("my_mod");
    let void_type = context.void_type();
    let i32_type = context.i32_type();
    let fn_type = void_type.fn_type(&[i32_type.into()], false);
    let fn_value = module.add_function("my_fn", fn_type, None);
    let entry_bb = fn_value.append_basic_block("entry");
    let string_attribute = context.create_string_attribute("my_key", "my_val");
    let alignstack_attribute = Attribute::get_named_enum_kind_id("alignstack");
    let enum_attribute = context.create_enum_attribute(alignstack_attribute, 1);

    builder.position_at_end(&entry_bb);

    let call_site_value = builder.build_call(fn_value, &[i32_type.const_int(1, false).into()], "my_fn");

    builder.build_return(None);

    assert_eq!(call_site_value.count_arguments(), 1);
    assert_eq!(call_site_value.count_attributes(0), 0);
    assert_eq!(call_site_value.count_attributes(1), 0);

    call_site_value.remove_string_attribute(0, "my_key"); // Noop
    call_site_value.remove_enum_attribute(0, alignstack_attribute); // Noop

    // define align 1 "my_key"="my_val" void @my_fn()
    call_site_value.add_attribute(0, string_attribute);
    call_site_value.add_attribute(1, string_attribute); // Applied to 1st param
    call_site_value.add_attribute(0, enum_attribute);

    assert_eq!(call_site_value.count_attributes(0), 2);
    assert_eq!(call_site_value.get_enum_attribute(0, alignstack_attribute), Some(enum_attribute));
    assert_eq!(call_site_value.get_string_attribute(0, "my_key"), Some(string_attribute));

    call_site_value.remove_string_attribute(0, "my_key");

    assert_eq!(call_site_value.count_attributes(0), 1);

    call_site_value.remove_enum_attribute(0, alignstack_attribute);

    assert_eq!(call_site_value.count_attributes(0), 0);
    assert!(call_site_value.get_enum_attribute(0, alignstack_attribute).is_none());
    assert!(call_site_value.get_string_attribute(0, "my_key").is_none());
    assert_eq!(call_site_value.get_called_fn_value(), fn_value);

    call_site_value.set_param_alignment_attribute(0, 16);

    assert_eq!(call_site_value.count_attributes(0), 1);
    assert!(call_site_value.get_enum_attribute(0, 1).is_some());
}
<|endoftext|>