<commit_before>//! Simple counting bloom filters.
#![license = "MIT"]
#![deny(missing_doc)]

extern crate rand;

use rand::Rng;
use std::hash::{hash,Hash};
use std::iter;
use std::num;
use std::uint;

/// A counting bloom filter.
///
/// A bloom filter is a probabilistic data structure which allows you to add and
/// remove elements from a set, query the set for whether it may contain an
/// element or definitely exclude it, and uses much less ram than an equivalent
/// hashtable.
pub struct BloomFilter {
    buf: Vec<uint>,
    number_of_insertions: uint,
}

// Here's where some of the magic numbers came from:
//
// m = number of elements in the filter
// n = size of the filter
// k = number of hash functions
//
// p = Pr[false positive] = 0.01 false positive rate
//
// if we have an estimation of the number of elements in the bloom filter, we
// know m.
//
// p = (1 - exp(-kn/m))^k
// k = (m/n)ln2
// lnp = -(m/n)(ln2)^2
// m = -nlnp/(ln2)^2
// => n = -m(ln2)^2/lnp
//     ~= 10*m
//
// k = (m/n)ln2 = 10ln2 ~= 7

static NUMBER_OF_HASHES: uint = 7;

static BITS_PER_BUCKET: uint = 4;
static BUCKETS_PER_WORD: uint = uint::BITS / BITS_PER_BUCKET;

/// Returns a tuple of (array index, lsr shift amount) to get to the bits you
/// need. Don't forget to mask with 0xF!
fn bucket_index_to_array_index(bucket_index: uint) -> (uint, uint) {
    let arr_index = bucket_index / BUCKETS_PER_WORD;
    let shift_amount = (bucket_index % BUCKETS_PER_WORD) * BITS_PER_BUCKET;
    (arr_index, shift_amount)
}

// Key Stretching
// ==============
//
// Siphash is expensive. Instead of running it `NUMBER_OF_HASHES`, which would
// be a pretty big hit on performance, we just use it to see a non-cryptographic
// random number generator. This stretches the hash to get us our
// `NUMBER_OF_HASHES` array indicies.
//
// A hash is a `u64` and comes from SipHash.
// A shash is a `uint` stretched hash which comes from the XorShiftRng.

fn to_rng(hash: u64) -> rand::XorShiftRng {
    let bottom = (hash & 0xFFFFFFFF) as u32;
    let top    = ((hash >> 32) & 0xFFFFFFFF) as u32;
    rand::SeedableRng::from_seed([ 0x97830e05, 0x113ba7bb, bottom, top ])
}

fn stretch<'a>(r: &'a mut rand::XorShiftRng)
  -> iter::Take<rand::Generator<'a, uint, rand::XorShiftRng>> {
    r.gen_iter().take(NUMBER_OF_HASHES)
}

impl BloomFilter {
    /// This bloom filter is tuned to have ~1% false positive rate. In exchange
    /// for this guarantee, you need to have a reasonable upper bound on the
    /// number of elements that will ever be inserted into it. If you guess too
    /// low, your false positive rate will suffer. If you guess too high, you'll
    /// use more memory than is really necessary.
    pub fn new(expected_number_of_insertions: uint) -> BloomFilter {
        let size_in_buckets = 10 * expected_number_of_insertions;

        let num_words =
            num::checked_next_power_of_two(size_in_buckets / BUCKETS_PER_WORD)
            .unwrap();

        BloomFilter {
            buf: Vec::from_elem(num_words, 0),
            number_of_insertions: 0,
        }
    }

    /// Since the array length must be a power of two, this will return a
    /// bitmask that can be `&`ed with a number to bring it into the range of
    /// the array.
    fn mask(&self) -> uint {
        (self.buf.len()*BUCKETS_PER_WORD) - 1 //guaranteed to be a power of two
    }

    /// Converts a stretched hash into a bucket index.
    fn shash_to_bucket_index(&self, shash: uint) -> uint {
        shash & self.mask()
    }

    /// Converts a stretched hash into an array and bit index. See the comment
    /// on `bucket_index_to_array_index` for details about the return value.
    fn shash_to_array_index(&self, shash: uint) -> (uint, uint) {
        bucket_index_to_array_index(self.shash_to_bucket_index(shash))
    }

    /// Gets the value at a given bucket.
    fn bucket_get(&self, a_idx: uint, shift_amount: uint) -> uint {
        let array_val = self.buf[a_idx];
        (array_val >> shift_amount) & 0xF
    }

    /// Sets the value at a given bucket. This will not bounds check, but that's
    /// ok because you've called `bucket_get` first, anyhow.
    fn bucket_set(&mut self, a_idx: uint, shift_amount: uint, new_val: uint) {
        // We can avoid bounds checking here since in order to do a bucket_set
        // we have to had done a `bucket_get` at the same index for it to make
        // sense.
        let old_val = self.buf.as_mut_slice().get_mut(a_idx).unwrap();
        let mask = (1 << BITS_PER_BUCKET) - 1;                // selects the right-most bucket
        let select_in_bucket = mask << shift_amount;          // selects the correct bucket
        let select_out_of_bucket = !select_in_bucket;         // selects everything except the correct bucket
        let new_array_val = (new_val << shift_amount)         // move the new_val into the right spot
                          | (*old_val & select_out_of_bucket); // mask out the old value, and or it with the new one
        *old_val = new_array_val;
    }

    /// Insert a stretched hash into the bloom filter, remembering to saturate
    /// the counter instead of overflowing.
    fn insert_shash(&mut self, shash: uint) {
        let (a_idx, shift_amount) = self.shash_to_array_index(shash);
        let b_val = self.bucket_get(a_idx, shift_amount);

        // saturate the count.
        if b_val == 0xF {
            return;
        }

        let new_val = b_val + 1;

        self.bucket_set(a_idx, shift_amount, new_val);
    }

    /// Insert a hashed value into the bloom filter.
    fn insert_hashed(&mut self, hash: u64) {
        self.number_of_insertions += 1;
        for h in stretch(&mut to_rng(hash)) {
            self.insert_shash(h);
        }
    }

    /// Inserts a value into the bloom filter. Note that the bloom filter isn't
    /// parameterized over the values it holds. That's because it can hold
    /// values of different types, as long as it can get a hash out of them.
    pub fn insert<H: Hash>(&mut self, h: &H) {
        self.insert_hashed(hash(h))
    }

    /// Removes a stretched hash from the bloom filter, taking care not to
    /// decrememnt saturated counters.
    ///
    /// It is an error to remove never-inserted elements.
    fn remove_shash(&mut self, shash: uint) {
        let (a_idx, shift_amount) = self.shash_to_array_index(shash);
        let b_val = self.bucket_get(a_idx, shift_amount);
        assert!(b_val != 0, "Removing an element that was never inserted.");

        // can't do anything if the counter saturated.
        if b_val == 0xF { return; }

        self.bucket_set(a_idx, shift_amount, b_val - 1);
    }

    /// Removes a hashed value from the bloom filter.
    fn remove_hashed(&mut self, hash: u64) {
        self.number_of_insertions -= 1;
        for h in stretch(&mut to_rng(hash)) {
            self.remove_shash(h);
        }
    }

    /// Removes a value from the bloom filter.
    ///
    /// Be careful of adding and removing lots of elements, especially for
    /// long-lived bloom filters. The counters in each bucket will saturate if
    /// 16 or more elements hash to it, and then stick there. This will hurt
    /// your false positive rate. To fix this, you might consider refreshing the
    /// bloom filter by `clear`ing it, and then reinserting elements at regular,
    /// long intervals.
    ///
    /// It is an error to remove never-inserted elements.
    pub fn remove<H: Hash>(&mut self, h: &H) {
        self.remove_hashed(hash(h))
    }

    /// Returns `true` if the bloom filter cannot possibly contain the given
    /// stretched hash.
    fn definitely_excludes_shash(&self, shash: uint) -> bool {
        let (a_idx, shift_amount) = self.shash_to_array_index(shash);
        self.bucket_get(a_idx, shift_amount) == 0
    }

    /// A hash is definitely excluded iff none of the stretched hashes are in
    /// the bloom filter.
    fn definitely_excludes_hashed(&self, hash: u64) -> bool {
        let mut ret = false;

        for shash in stretch(&mut to_rng(hash)) {
            ret |= self.definitely_excludes_shash(shash);
        }

        ret
    }

    /// A bloom filter can tell you whether or not a value has definitely never
    /// been inserted. Note that bloom filters can give false positives.
    pub fn definitely_excludes<H: Hash>(&self, h: &H) -> bool {
        self.definitely_excludes_hashed(hash(h))
    }

    /// A bloom filter can tell you if an element /may/ be in it. It cannot be
    /// certain. But, assuming correct usage, this query will have a low false
    // positive rate.
    pub fn may_include<H: Hash>(&self, h: &H) -> bool {
        !self.definitely_excludes(h)
    }

    /// Returns the number of elements ever inserted into the bloom filter - the
    /// number of elements removed.
    pub fn number_of_insertions(&self) -> uint {
        self.number_of_insertions
    }

    /// Returns the number of bytes of memory the bloom filter uses.
    pub fn size(&self) -> uint {
        self.buf.len() * uint::BYTES
    }

    /// Removes all elements from the bloom filter. This is both more efficient
    /// and has better false-positive properties than repeatedly calling `remove`
    /// on every element.
    pub fn clear(&mut self) {
        self.number_of_insertions = 0;
        for x in self.buf.as_mut_slice().mut_iter() {
            *x = 0u;
        }
    }
}

#[test]
fn create_and_insert_some_stuff() {
    use std::iter::range;

    let mut bf = BloomFilter::new(1000);

    for i in range(0u, 1000) {
        bf.insert(&i);
    }

    assert_eq!(bf.number_of_insertions(), 1000);

    for i in range(0u, 1000) {
        assert!(bf.may_include(&i));
    }

    let false_positives =
        range(1001u, 2000).filter(|i| bf.may_include(&i)).count();

    assert!(false_positives < 10) // 1%.

    for i in range(0u, 100) {
        bf.remove(&i);
    }

    assert_eq!(bf.number_of_insertions(), 900);

    for i in range(100u, 1000) {
        assert!(bf.may_include(&i));
    }

    let false_positives = range(0u, 100).filter(|i| bf.may_include(&i)).count();

    assert!(false_positives < 2); // 2%.

    bf.clear();

    assert_eq!(bf.number_of_insertions(), 0);

    for i in range(0u, 2000) {
        assert!(bf.definitely_excludes(&i));
    }
}

#[cfg(test)]
mod bench {
    extern crate test;

    use std::hash::hash;
    use std::iter;
    use super::BloomFilter;

    #[bench]
    fn create_insert_1000_remove_100_lookup_100(b: &mut test::Bencher) {
        b.iter(|| {
            let mut bf = BloomFilter::new(1000);
            for i in iter::range(0u, 1000) {
                bf.insert(&i);
            }
            for i in iter::range(0u, 100) {
                bf.remove(&i);
            }
            for i in iter::range(100u, 200) {
                test::black_box(bf.may_include(&i));
            }
        });
    }

    #[bench]
    fn may_include(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new(1000);

        for i in iter::range(0u, 1000) {
            bf.insert(&i);
        }

        let mut i = 0u;

        b.bench_n(1000, |b| {
            b.iter(|| {
                test::black_box(bf.may_include(&i));
                i += 1;
            });
        });
    }

    #[bench]
    fn insert(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new(1000);

        b.bench_n(1000, |b| {
            let mut i = 0u;

            b.iter(|| {
                test::black_box(bf.insert(&i));
                i += 1;
            });
        });
    }

    #[bench]
    fn remove(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new(1000);
        for i in range(0u, 1000) {
            bf.insert(&i);
        }

        b.bench_n(1000, |b| {
            let mut i = 0u;

            b.iter(|| {
                bf.remove(&i);
                i += 1;
            });
        });

        test::black_box(bf.may_include(&0u));
    }

    #[bench]
    fn hash_a_uint(b: &mut test::Bencher) {
        let mut i = 0u;
        b.iter(|| {
            test::black_box(hash(&i));
            i += 1;
        })
    }
}
<commit_msg>derive Clone<commit_after>//! Simple counting bloom filters.
#![license = "MIT"]
#![deny(missing_doc)]

extern crate rand;

use rand::Rng;
use std::hash::{hash,Hash};
use std::iter;
use std::num;
use std::uint;

/// A counting bloom filter.
///
/// A bloom filter is a probabilistic data structure which allows you to add and
/// remove elements from a set, query the set for whether it may contain an
/// element or definitely exclude it, and uses much less ram than an equivalent
/// hashtable.
#[deriving(Clone)]
pub struct BloomFilter {
    buf: Vec<uint>,
    number_of_insertions: uint,
}

// Here's where some of the magic numbers came from:
//
// m = number of elements in the filter
// n = size of the filter
// k = number of hash functions
//
// p = Pr[false positive] = 0.01 false positive rate
//
// if we have an estimation of the number of elements in the bloom filter, we
// know m.
//
// p = (1 - exp(-kn/m))^k
// k = (m/n)ln2
// lnp = -(m/n)(ln2)^2
// m = -nlnp/(ln2)^2
// => n = -m(ln2)^2/lnp
//     ~= 10*m
//
// k = (m/n)ln2 = 10ln2 ~= 7

static NUMBER_OF_HASHES: uint = 7;

static BITS_PER_BUCKET: uint = 4;
static BUCKETS_PER_WORD: uint = uint::BITS / BITS_PER_BUCKET;

/// Returns a tuple of (array index, lsr shift amount) to get to the bits you
/// need. Don't forget to mask with 0xF!
fn bucket_index_to_array_index(bucket_index: uint) -> (uint, uint) {
    let arr_index = bucket_index / BUCKETS_PER_WORD;
    let shift_amount = (bucket_index % BUCKETS_PER_WORD) * BITS_PER_BUCKET;
    (arr_index, shift_amount)
}

// Key Stretching
// ==============
//
// Siphash is expensive. Instead of running it `NUMBER_OF_HASHES`, which would
// be a pretty big hit on performance, we just use it to see a non-cryptographic
// random number generator. This stretches the hash to get us our
// `NUMBER_OF_HASHES` array indicies.
//
// A hash is a `u64` and comes from SipHash.
// A shash is a `uint` stretched hash which comes from the XorShiftRng.

fn to_rng(hash: u64) -> rand::XorShiftRng {
    let bottom = (hash & 0xFFFFFFFF) as u32;
    let top    = ((hash >> 32) & 0xFFFFFFFF) as u32;
    rand::SeedableRng::from_seed([ 0x97830e05, 0x113ba7bb, bottom, top ])
}

fn stretch<'a>(r: &'a mut rand::XorShiftRng)
  -> iter::Take<rand::Generator<'a, uint, rand::XorShiftRng>> {
    r.gen_iter().take(NUMBER_OF_HASHES)
}

impl BloomFilter {
    /// This bloom filter is tuned to have ~1% false positive rate. In exchange
    /// for this guarantee, you need to have a reasonable upper bound on the
    /// number of elements that will ever be inserted into it. If you guess too
    /// low, your false positive rate will suffer. If you guess too high, you'll
    /// use more memory than is really necessary.
    pub fn new(expected_number_of_insertions: uint) -> BloomFilter {
        let size_in_buckets = 10 * expected_number_of_insertions;

        let num_words =
            num::checked_next_power_of_two(size_in_buckets / BUCKETS_PER_WORD)
            .unwrap();

        BloomFilter {
            buf: Vec::from_elem(num_words, 0),
            number_of_insertions: 0,
        }
    }

    /// Since the array length must be a power of two, this will return a
    /// bitmask that can be `&`ed with a number to bring it into the range of
    /// the array.
    fn mask(&self) -> uint {
        (self.buf.len()*BUCKETS_PER_WORD) - 1 //guaranteed to be a power of two
    }

    /// Converts a stretched hash into a bucket index.
    fn shash_to_bucket_index(&self, shash: uint) -> uint {
        shash & self.mask()
    }

    /// Converts a stretched hash into an array and bit index. See the comment
    /// on `bucket_index_to_array_index` for details about the return value.
    fn shash_to_array_index(&self, shash: uint) -> (uint, uint) {
        bucket_index_to_array_index(self.shash_to_bucket_index(shash))
    }

    /// Gets the value at a given bucket.
    fn bucket_get(&self, a_idx: uint, shift_amount: uint) -> uint {
        let array_val = self.buf[a_idx];
        (array_val >> shift_amount) & 0xF
    }

    /// Sets the value at a given bucket. This will not bounds check, but that's
    /// ok because you've called `bucket_get` first, anyhow.
    fn bucket_set(&mut self, a_idx: uint, shift_amount: uint, new_val: uint) {
        // We can avoid bounds checking here since in order to do a bucket_set
        // we have to had done a `bucket_get` at the same index for it to make
        // sense.
        let old_val = self.buf.as_mut_slice().get_mut(a_idx).unwrap();
        let mask = (1 << BITS_PER_BUCKET) - 1;                // selects the right-most bucket
        let select_in_bucket = mask << shift_amount;          // selects the correct bucket
        let select_out_of_bucket = !select_in_bucket;         // selects everything except the correct bucket
        let new_array_val = (new_val << shift_amount)         // move the new_val into the right spot
                          | (*old_val & select_out_of_bucket); // mask out the old value, and or it with the new one
        *old_val = new_array_val;
    }

    /// Insert a stretched hash into the bloom filter, remembering to saturate
    /// the counter instead of overflowing.
    fn insert_shash(&mut self, shash: uint) {
        let (a_idx, shift_amount) = self.shash_to_array_index(shash);
        let b_val = self.bucket_get(a_idx, shift_amount);

        // saturate the count.
        if b_val == 0xF {
            return;
        }

        let new_val = b_val + 1;

        self.bucket_set(a_idx, shift_amount, new_val);
    }

    /// Insert a hashed value into the bloom filter.
    fn insert_hashed(&mut self, hash: u64) {
        self.number_of_insertions += 1;
        for h in stretch(&mut to_rng(hash)) {
            self.insert_shash(h);
        }
    }

    /// Inserts a value into the bloom filter. Note that the bloom filter isn't
    /// parameterized over the values it holds. That's because it can hold
    /// values of different types, as long as it can get a hash out of them.
    pub fn insert<H: Hash>(&mut self, h: &H) {
        self.insert_hashed(hash(h))
    }

    /// Removes a stretched hash from the bloom filter, taking care not to
    /// decrememnt saturated counters.
    ///
    /// It is an error to remove never-inserted elements.
    fn remove_shash(&mut self, shash: uint) {
        let (a_idx, shift_amount) = self.shash_to_array_index(shash);
        let b_val = self.bucket_get(a_idx, shift_amount);
        assert!(b_val != 0, "Removing an element that was never inserted.");

        // can't do anything if the counter saturated.
        if b_val == 0xF { return; }

        self.bucket_set(a_idx, shift_amount, b_val - 1);
    }

    /// Removes a hashed value from the bloom filter.
    fn remove_hashed(&mut self, hash: u64) {
        self.number_of_insertions -= 1;
        for h in stretch(&mut to_rng(hash)) {
            self.remove_shash(h);
        }
    }

    /// Removes a value from the bloom filter.
    ///
    /// Be careful of adding and removing lots of elements, especially for
    /// long-lived bloom filters. The counters in each bucket will saturate if
    /// 16 or more elements hash to it, and then stick there. This will hurt
    /// your false positive rate. To fix this, you might consider refreshing the
    /// bloom filter by `clear`ing it, and then reinserting elements at regular,
    /// long intervals.
    ///
    /// It is an error to remove never-inserted elements.
    pub fn remove<H: Hash>(&mut self, h: &H) {
        self.remove_hashed(hash(h))
    }

    /// Returns `true` if the bloom filter cannot possibly contain the given
    /// stretched hash.
    fn definitely_excludes_shash(&self, shash: uint) -> bool {
        let (a_idx, shift_amount) = self.shash_to_array_index(shash);
        self.bucket_get(a_idx, shift_amount) == 0
    }

    /// A hash is definitely excluded iff none of the stretched hashes are in
    /// the bloom filter.
    fn definitely_excludes_hashed(&self, hash: u64) -> bool {
        let mut ret = false;

        for shash in stretch(&mut to_rng(hash)) {
            ret |= self.definitely_excludes_shash(shash);
        }

        ret
    }

    /// A bloom filter can tell you whether or not a value has definitely never
    /// been inserted. Note that bloom filters can give false positives.
    pub fn definitely_excludes<H: Hash>(&self, h: &H) -> bool {
        self.definitely_excludes_hashed(hash(h))
    }

    /// A bloom filter can tell you if an element /may/ be in it. It cannot be
    /// certain. But, assuming correct usage, this query will have a low false
    // positive rate.
    pub fn may_include<H: Hash>(&self, h: &H) -> bool {
        !self.definitely_excludes(h)
    }

    /// Returns the number of elements ever inserted into the bloom filter - the
    /// number of elements removed.
    pub fn number_of_insertions(&self) -> uint {
        self.number_of_insertions
    }

    /// Returns the number of bytes of memory the bloom filter uses.
    pub fn size(&self) -> uint {
        self.buf.len() * uint::BYTES
    }

    /// Removes all elements from the bloom filter. This is both more efficient
    /// and has better false-positive properties than repeatedly calling `remove`
    /// on every element.
    pub fn clear(&mut self) {
        self.number_of_insertions = 0;
        for x in self.buf.as_mut_slice().mut_iter() {
            *x = 0u;
        }
    }
}

#[test]
fn create_and_insert_some_stuff() {
    use std::iter::range;

    let mut bf = BloomFilter::new(1000);

    for i in range(0u, 1000) {
        bf.insert(&i);
    }

    assert_eq!(bf.number_of_insertions(), 1000);

    for i in range(0u, 1000) {
        assert!(bf.may_include(&i));
    }

    let false_positives =
        range(1001u, 2000).filter(|i| bf.may_include(&i)).count();

    assert!(false_positives < 10) // 1%.

    for i in range(0u, 100) {
        bf.remove(&i);
    }

    assert_eq!(bf.number_of_insertions(), 900);

    for i in range(100u, 1000) {
        assert!(bf.may_include(&i));
    }

    let false_positives = range(0u, 100).filter(|i| bf.may_include(&i)).count();

    assert!(false_positives < 2); // 2%.

    bf.clear();

    assert_eq!(bf.number_of_insertions(), 0);

    for i in range(0u, 2000) {
        assert!(bf.definitely_excludes(&i));
    }
}

#[cfg(test)]
mod bench {
    extern crate test;

    use std::hash::hash;
    use std::iter;
    use super::BloomFilter;

    #[bench]
    fn create_insert_1000_remove_100_lookup_100(b: &mut test::Bencher) {
        b.iter(|| {
            let mut bf = BloomFilter::new(1000);
            for i in iter::range(0u, 1000) {
                bf.insert(&i);
            }
            for i in iter::range(0u, 100) {
                bf.remove(&i);
            }
            for i in iter::range(100u, 200) {
                test::black_box(bf.may_include(&i));
            }
        });
    }

    #[bench]
    fn may_include(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new(1000);

        for i in iter::range(0u, 1000) {
            bf.insert(&i);
        }

        let mut i = 0u;

        b.bench_n(1000, |b| {
            b.iter(|| {
                test::black_box(bf.may_include(&i));
                i += 1;
            });
        });
    }

    #[bench]
    fn insert(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new(1000);

        b.bench_n(1000, |b| {
            let mut i = 0u;

            b.iter(|| {
                test::black_box(bf.insert(&i));
                i += 1;
            });
        });
    }

    #[bench]
    fn remove(b: &mut test::Bencher) {
        let mut bf = BloomFilter::new(1000);
        for i in range(0u, 1000) {
            bf.insert(&i);
        }

        b.bench_n(1000, |b| {
            let mut i = 0u;

            b.iter(|| {
                bf.remove(&i);
                i += 1;
            });
        });

        test::black_box(bf.may_include(&0u));
    }

    #[bench]
    fn hash_a_uint(b: &mut test::Bencher) {
        let mut i = 0u;
        b.iter(|| {
            test::black_box(hash(&i));
            i += 1;
        })
    }
}
<|endoftext|>


<commit_before>extern crate libsvm_sys;
extern crate libc;

use std::ops::Deref;

use std::path::Path;
use std::marker::PhantomData;

/// The node type used inside libsvm
pub type SvmNode = libsvm_sys::SvmNode;

/// A wrapper around the sparse vectors used in libsvm
pub struct SparseVector {
    pub inner: Vec<SvmNode>,
}

impl <'a> From<&'a [f64]> for SparseVector {
    fn from(fs: &[f64]) -> SparseVector {
        let vec = fs.iter()
            .enumerate()
            .filter(|&(_, x)| *x != 0.0)
            .map(|(idx, x)| SvmNode {
                index: idx as libc::c_int,
                value: *x as libc::c_double,
            })
            .collect();
        SparseVector {
            inner: vec,
        }
    }
}

#[repr(C)]
pub enum SvmType {
    CSvc,
    NuSvc,
    OneClass,
    EpsilonSvr,
    NuSvr,
}

#[repr(C)]
pub enum KernelType {
    Linear,
    Poly,
    Rbf,
    Sigmoid,
    Precomputed,
}

impl SvmType {
    pub fn from_integral<T: Into<libc::c_int>>(t: T) -> Option<SvmType> {
        match t.into() {
            libsvm_sys::C_SVC =>       Some(SvmType::CSvc),
            libsvm_sys::NU_SVC =>      Some(SvmType::NuSvc),
            libsvm_sys::ONE_CLASS =>   Some(SvmType::OneClass),
            libsvm_sys::EPSILON_SVR => Some(SvmType::EpsilonSvr),
            libsvm_sys::NU_SVR =>      Some(SvmType::NuSvr),
            _ => None,
        }
    }
}

impl KernelType {
    pub fn from_integral<T: Into<libc::c_int>>(t: T) -> Option<KernelType> {
        match t.into() {
            libsvm_sys::LINEAR =>       Some(KernelType::Linear),
            libsvm_sys::POLY =>         Some(KernelType::Poly),
            libsvm_sys::RBF =>          Some(KernelType::Rbf),
            libsvm_sys::SIGMOID =>      Some(KernelType::Sigmoid),
            libsvm_sys::PRECOMPUTED =>  Some(KernelType::Precomputed),
            _ => None,
        }
    }
}

struct Problem<'a> {
    x: RefOrOwned<'a, SparseVector>,
    // needed to maintain the array of pointers in case svm_train uses that array
    x_raw: Vec<*const SvmNode>,
    y: RefOrOwned<'a, libc::c_double>,
}

enum RefOrOwned<'a, T: 'a> {
    Ref(&'a [T]),
    Owned(Vec<T>),
}

impl <'a, T> Deref for RefOrOwned<'a, T> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        match self {
            &RefOrOwned::Ref(ref s) => s,
            &RefOrOwned::Owned(ref v) => &v,
        }
    }
}

pub struct Model<'a> {
    inner: *mut libsvm_sys::SvmModel,
    data: Option<Problem<'a>>,
}

pub struct Parameters<'a> {
    inner: libsvm_sys::SvmParameter,
    marker_: PhantomData<&'a [libc::c_double]>,
}

#[derive(Clone, Debug)]
pub enum ParameterCheckError {
    UnknownSvmType,
    UnknownKernelType,
    GammaNonpositive,
    KernelDegreeNonpositive,
    CacheSizeNonpositive,
    InvalidNu,
    PNonpositive,
    InvalidShrinking,
    InvalidProbability,
    Unsupported,
    InfeasibleNu,
}

#[derive(Clone, Debug)]
pub enum ModelCreationError {
    ParameterCheckError(ParameterCheckError),
    UnableToLoadModel,
    UnableToTrainModel,
}

#[derive(Clone, Debug)]
pub struct ModelSaveError;

impl <'a> Default for Parameters<'a> {
    fn default() -> Parameters<'a> {
        // TODO: use better default parameters
        Parameters {
            inner: libsvm_sys::SvmParameter {
                svm_type: SvmType::CSvc as libc::c_int,
                kernel_type: KernelType::Rbf as libc::c_int,
                degree: 0,
                gamma: 0.5,
                coef0: 0.0,

                cache_size: 64.0,
                eps: 1e-3,
                c: 1.0,
                nr_weight: 0,
                weight_label: 0 as *mut libc::c_int,
                weight: 0 as *mut libc::c_double,
                nu: 0.0,
                p: 0.0,
                shrinking: 0,
                probability: 0,
            },
            marker_: PhantomData,
        }
    }
}

/// A typesafe wrapper about libsvm that ensures that it is correctly
/// configured
impl <'a> Parameters<'a> {
    pub fn new() -> Parameters<'a> {
        Parameters::default()
    }

    pub fn linear(self) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.kernel_type = KernelType::Linear as libc::c_int;
        ret
    }

    pub fn poly(self, gamma: f64, coef0: f64, degree: isize) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.kernel_type = KernelType::Poly as libc::c_int;
        ret.inner.gamma = gamma as libc::c_double;
        ret.inner.coef0 = coef0 as libc::c_double;
        ret.inner.degree = degree as libc::c_int;
        ret
    }

    pub fn rbf(self, gamma: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.kernel_type = KernelType::Rbf as libc::c_int;
        ret.inner.gamma = gamma as libc::c_double;
        ret
    }

    pub fn sigmoid(self, gamma: f64, coef: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.kernel_type = KernelType::Sigmoid as libc::c_int;
        ret.inner.gamma = gamma as libc::c_double;
        ret
    }

    pub fn precomputed(self) -> Parameters<'a> {
        // TODO: figure out a way to nicely wrap precomputed kernel semantics
        unimplemented!()
    }

    pub fn c_svc<'b>(self,
                     c: f64,
                     idxs: &'b [libc::c_int],
                     weights: &'b [libc::c_double]) -> Parameters<'b> {
        let mut ret = Parameters {
            inner: self.inner,
            marker_: PhantomData,
        };
        ret.inner.svm_type = SvmType::CSvc as libc::c_int;
        ret.inner.c = c;
        assert_eq!(idxs.len(), weights.len());
        ret.inner.nr_weight = idxs.len() as libc::c_int;
        ret.inner.weight_label = idxs.as_ptr();
        ret.inner.weight = weights.as_ptr();
        ret
    }

    pub fn nu_svc(self, nu: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.svm_type = SvmType::NuSvc as libc::c_int;
        ret.inner.nu = nu as libc::c_double;
        ret
    }

    pub fn one_class(self, nu: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.svm_type = SvmType::OneClass as libc::c_int;
        ret.inner.nu = nu as libc::c_double;
        ret
    }

    pub fn epsilon_svr(self, c: f64, p: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.svm_type = SvmType::EpsilonSvr as libc::c_int;
        ret.inner.c = c as libc::c_double;
        ret.inner.p = p as libc::c_double;
        ret
    }

    pub fn nu_svr(self, c: f64, nu: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.svm_type = SvmType::NuSvr as libc::c_int;
        ret.inner.c = c as libc::c_double;
        ret.inner.nu = nu as libc::c_double;
        ret
    }

    /// Sets the cache size used by libsvm in MB
    pub fn cache_size(self, sz: libc::c_double) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.cache_size = sz;
        ret
    }

    pub fn use_shrinking(self, b: bool) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.shrinking = if b { 1 } else { 0 };
        ret
    }

    pub fn estimate_probability(self, b: bool) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.probability = if b { 1 } else { 0 };
        ret
    }
}

impl <'a> Model<'a> {
    pub fn train<'b, 'c, T, C>(data: &[(&T, C)], parameters: &Parameters<'b>) -> Result<Model<'a>, ModelCreationError> where
        T: AsRef<&'c [f64]>, C: Copy + Into<libc::c_double>
    {
        // copy the data into a vector of sparse vectors
        let sparse: Vec<SparseVector> = data.iter()
            .map(|&(x, _)| SparseVector::from(*x.as_ref()))
            .collect();
        let classes = data.iter()
            .map(|&(_, y)| y.into())
            .collect();
        let sparse_ptr = sparse.iter()
            .map(|x| x.inner.as_ptr())
            .collect();

        let problem = Problem {
            x: RefOrOwned::Owned(sparse),
            x_raw: sparse_ptr,
            y: RefOrOwned::Owned(classes),
        };
        let svm_problem = libsvm_sys::SvmProblem {
            l: problem.x.len() as libc::c_int,
            y: problem.y.as_ptr(),
            x: problem.x_raw.as_ptr(),
        };
        // check parameters
        let parameter_error = unsafe {
            libsvm_sys::svm_check_parameter(
                &svm_problem as *const libsvm_sys::SvmProblem,
                &parameters.inner as *const libsvm_sys::SvmParameter)
        };
        // check result
        if parameter_error != std::ptr::null() {
            // TODO
        }
        // train the SVM model
        let svm_model = unsafe {
            libsvm_sys::svm_train(
                &svm_problem as *const libsvm_sys::SvmProblem,
                &parameters.inner as *const libsvm_sys::SvmParameter)
        };
        // check result
        if svm_model.is_null() {
            Err(ModelCreationError::UnableToTrainModel)
        } else {
            Ok(Model {
                inner: svm_model,
                data: Some(problem),
            })
        }
    }

    pub fn train_sparse<'b, C>(data: &[(&'a SparseVector, C)], parameters: &Parameters<'b>) -> Result<Model<'a>, ModelCreationError> where
        C: Copy + Into<u32>
    {
        // TODO: allow for zero copy training
        unimplemented!()
    }

    // TODO: make more type safe; should be using a Path instead of a &str
    pub fn load_model(path: &str) -> Result<Model<'a>, ModelCreationError> {
        let model = unsafe {
            libsvm_sys::svm_load_model(path.as_ptr() as *const libc::c_char)
        };

        if model.is_null() {
            Err(ModelCreationError::UnableToLoadModel)
        } else {
            Ok(Model {
                inner: model,
                data: None,
            })
        }
    }

    pub fn save_model(&self, path: &str) -> Result<(), ModelSaveError> {
        let ret = unsafe {
            libsvm_sys::svm_save_model(
                path.as_ptr() as *const libc::c_char,
                self.inner as *const libsvm_sys::SvmModel)
        };

        if ret < 0 {
            Err(ModelSaveError)
        } else {
            Ok(())
        }
    }

    pub fn svm_type(&self) -> Option<SvmType> {
        let ret = unsafe {
            libsvm_sys::svm_get_svm_type(self.inner as *const libsvm_sys::SvmModel)
        };
        SvmType::from_integral(ret)
    }

    pub fn nr_class(&self) -> libc::c_int {
        unsafe {
            libsvm_sys::svm_get_nr_class(self.inner as *const libsvm_sys::SvmModel)
        }
    }

    // TODO: provide type safe wrappers for labels and sv_indices
    // need a way to see if the model has labels or sv_indices
    pub fn labels(&self) -> Vec<libc::c_int> {
        unimplemented!()
    }

    pub fn sv_indices(&self) -> Vec<libc::c_int> {
        unimplemented!()
    }

    pub fn predict(&self, data: &[f64]) -> libc::c_double {
        let sparse = SparseVector::from(data);
        unsafe {
            libsvm_sys::svm_predict(
                self.inner as *const libsvm_sys::SvmModel,
                sparse.inner.as_ptr()
            )
        }
    }

    pub fn predict_sparse(&self, data: &SparseVector) -> libc::c_double {
        unimplemented!()
    }
}

impl <'a> Drop for Model<'a> {
    fn drop(&mut self) {
        let mut ptr = self.inner as *mut libsvm_sys::SvmModel;
        unsafe {
            libsvm_sys::svm_free_and_destroy_model(&mut ptr as *mut *mut libsvm_sys::SvmModel);
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Fix sparse vector generation<commit_after>extern crate libsvm_sys;
extern crate libc;

use std::borrow::Borrow;

use std::ops::Deref;

use std::marker::PhantomData;

/// The node type used inside libsvm
pub type SvmNode = libsvm_sys::SvmNode;

/// A wrapper around the sparse vectors used in libsvm
pub struct SparseVector {
    pub inner: Vec<SvmNode>,
}

impl <'a> From<&'a [f64]> for SparseVector {
    fn from(fs: &[f64]) -> SparseVector {
        let vec = fs.iter()
            .enumerate()
            .filter(|&(_, x)| *x != 0.0)
            .map(|(idx, x)| SvmNode {
                index: idx as libc::c_int,
                value: *x as libc::c_double,
            })
            .chain(Some(SvmNode {
                index: -1 as libc::c_int,
                value: 0.0,
            }))
            .collect();
        SparseVector {
            inner: vec,
        }
    }
}

impl Deref for SparseVector {
    type Target = Vec<SvmNode>;
    fn deref(&self) -> &Vec<SvmNode> {
        &self.inner
    }
}

#[repr(C)]
pub enum SvmType {
    CSvc,
    NuSvc,
    OneClass,
    EpsilonSvr,
    NuSvr,
}

#[repr(C)]
pub enum KernelType {
    Linear,
    Poly,
    Rbf,
    Sigmoid,
    Precomputed,
}

impl SvmType {
    pub fn from_integral<T: Into<libc::c_int>>(t: T) -> Option<SvmType> {
        match t.into() {
            libsvm_sys::C_SVC =>       Some(SvmType::CSvc),
            libsvm_sys::NU_SVC =>      Some(SvmType::NuSvc),
            libsvm_sys::ONE_CLASS =>   Some(SvmType::OneClass),
            libsvm_sys::EPSILON_SVR => Some(SvmType::EpsilonSvr),
            libsvm_sys::NU_SVR =>      Some(SvmType::NuSvr),
            _ => None,
        }
    }
}

impl KernelType {
    pub fn from_integral<T: Into<libc::c_int>>(t: T) -> Option<KernelType> {
        match t.into() {
            libsvm_sys::LINEAR =>       Some(KernelType::Linear),
            libsvm_sys::POLY =>         Some(KernelType::Poly),
            libsvm_sys::RBF =>          Some(KernelType::Rbf),
            libsvm_sys::SIGMOID =>      Some(KernelType::Sigmoid),
            libsvm_sys::PRECOMPUTED =>  Some(KernelType::Precomputed),
            _ => None,
        }
    }
}

struct Problem<'a> {
    x: RefOrOwned<'a, SparseVector>,
    // needed to maintain the array of pointers in case svm_train uses that array
    x_raw: Vec<*const SvmNode>,
    y: RefOrOwned<'a, libc::c_double>,
}

enum RefOrOwned<'a, T: 'a> {
    Ref(&'a [T]),
    Owned(Vec<T>),
}

impl <'a, T> Deref for RefOrOwned<'a, T> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        match self {
            &RefOrOwned::Ref(ref s) => s,
            &RefOrOwned::Owned(ref v) => &v,
        }
    }
}

pub struct Model<'a> {
    inner: *mut libsvm_sys::SvmModel,
    data: Option<Problem<'a>>,
}

pub struct Parameters<'a> {
    inner: libsvm_sys::SvmParameter,
    marker_: PhantomData<&'a [libc::c_double]>,
}

#[derive(Clone, Debug)]
pub enum ParameterCheckError {
    UnknownSvmType,
    UnknownKernelType,
    GammaNonpositive,
    KernelDegreeNonpositive,
    CacheSizeNonpositive,
    InvalidNu,
    PNonpositive,
    InvalidShrinking,
    InvalidProbability,
    Unsupported,
    InfeasibleNu,
}

#[derive(Clone, Debug)]
pub enum ModelCreationError {
    ParameterCheckError(ParameterCheckError),
    UnableToLoadModel,
    UnableToTrainModel,
}

#[derive(Clone, Debug)]
pub struct ModelSaveError;

impl <'a> Default for Parameters<'a> {
    fn default() -> Parameters<'a> {
        // TODO: use better default parameters
        Parameters {
            inner: libsvm_sys::SvmParameter {
                svm_type: SvmType::CSvc as libc::c_int,
                kernel_type: KernelType::Rbf as libc::c_int,
                degree: 0,
                gamma: 0.5,
                coef0: 0.0,

                cache_size: 64.0,
                eps: 1e-3,
                c: 1.0,
                nr_weight: 0,
                weight_label: 0 as *mut libc::c_int,
                weight: 0 as *mut libc::c_double,
                nu: 0.0,
                p: 0.0,
                shrinking: 0,
                probability: 0,
            },
            marker_: PhantomData,
        }
    }
}

/// A typesafe wrapper about libsvm that ensures that it is correctly
/// configured
impl <'a> Parameters<'a> {
    pub fn new() -> Parameters<'a> {
        Parameters::default()
    }

    pub fn linear(self) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.kernel_type = KernelType::Linear as libc::c_int;
        ret
    }

    pub fn poly(self, gamma: f64, coef0: f64, degree: isize) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.kernel_type = KernelType::Poly as libc::c_int;
        ret.inner.gamma = gamma as libc::c_double;
        ret.inner.coef0 = coef0 as libc::c_double;
        ret.inner.degree = degree as libc::c_int;
        ret
    }

    pub fn rbf(self, gamma: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.kernel_type = KernelType::Rbf as libc::c_int;
        ret.inner.gamma = gamma as libc::c_double;
        ret
    }

    pub fn sigmoid(self, gamma: f64, coef: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.kernel_type = KernelType::Sigmoid as libc::c_int;
        ret.inner.gamma = gamma as libc::c_double;
        ret.inner.coef0 = coef as libc::c_double;
        ret
    }

    pub fn precomputed(self) -> Parameters<'a> {
        // TODO: figure out a way to nicely wrap precomputed kernel semantics
        unimplemented!()
    }

    pub fn c_svc<'b>(self,
                     c: f64,
                     idxs: &'b [libc::c_int],
                     weights: &'b [libc::c_double]) -> Parameters<'b> {
        let mut ret = Parameters {
            inner: self.inner,
            marker_: PhantomData,
        };
        ret.inner.svm_type = SvmType::CSvc as libc::c_int;
        ret.inner.c = c;
        assert_eq!(idxs.len(), weights.len());
        ret.inner.nr_weight = idxs.len() as libc::c_int;
        ret.inner.weight_label = idxs.as_ptr();
        ret.inner.weight = weights.as_ptr();
        ret
    }

    pub fn nu_svc(self, nu: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.svm_type = SvmType::NuSvc as libc::c_int;
        ret.inner.nu = nu as libc::c_double;
        ret
    }

    pub fn one_class(self, nu: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.svm_type = SvmType::OneClass as libc::c_int;
        ret.inner.nu = nu as libc::c_double;
        ret
    }

    pub fn epsilon_svr(self, c: f64, p: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.svm_type = SvmType::EpsilonSvr as libc::c_int;
        ret.inner.c = c as libc::c_double;
        ret.inner.p = p as libc::c_double;
        ret
    }

    pub fn nu_svr(self, c: f64, nu: f64) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.svm_type = SvmType::NuSvr as libc::c_int;
        ret.inner.c = c as libc::c_double;
        ret.inner.nu = nu as libc::c_double;
        ret
    }

    /// Sets the cache size used by libsvm in MB
    pub fn cache_size(self, sz: libc::c_double) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.cache_size = sz;
        ret
    }

    pub fn use_shrinking(self, b: bool) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.shrinking = if b { 1 } else { 0 };
        ret
    }

    pub fn estimate_probability(self, b: bool) -> Parameters<'a> {
        let mut ret = self;
        ret.inner.probability = if b { 1 } else { 0 };
        ret
    }
}

impl <'a> Model<'a> {
    pub fn train<'b, T, C>(data: &[(T, C)], parameters: &Parameters<'b>) -> Result<Model<'a>, ModelCreationError> where
        T: Borrow<[f64]>, C: Copy + Into<libc::c_double>
    {
        // copy the data into a vector of sparse vectors
        let sparse: Vec<SparseVector> = data.iter()
            .map(|&(ref x, _)| SparseVector::from(x.borrow()))
            .collect();
        let classes = data.iter()
            .map(|&(_, y)| y.into())
            .collect();
        let sparse_ptr = sparse.iter()
            .map(|x| x.inner.as_ptr())
            .collect();

        let problem = Problem {
            x: RefOrOwned::Owned(sparse),
            x_raw: sparse_ptr,
            y: RefOrOwned::Owned(classes),
        };
        let svm_problem = libsvm_sys::SvmProblem {
            l: problem.x.len() as libc::c_int,
            y: problem.y.as_ptr(),
            x: problem.x_raw.as_ptr(),
        };
        // check parameters
        let parameter_error = unsafe {
            libsvm_sys::svm_check_parameter(
                &svm_problem as *const libsvm_sys::SvmProblem,
                &parameters.inner as *const libsvm_sys::SvmParameter)
        };
        // check result
        if parameter_error != std::ptr::null() {
            // TODO
        }
        // train the SVM model
        let svm_model = unsafe {
            libsvm_sys::svm_train(
                &svm_problem as *const libsvm_sys::SvmProblem,
                &parameters.inner as *const libsvm_sys::SvmParameter)
        };
        // check result
        if svm_model.is_null() {
            Err(ModelCreationError::UnableToTrainModel)
        } else {
            Ok(Model {
                inner: svm_model,
                data: Some(problem),
            })
        }
    }

    pub fn train_sparse<'b, C>(data: &[(&'a SparseVector, C)], parameters: &Parameters<'b>) -> Result<Model<'a>, ModelCreationError> where
        C: Copy + Into<u32>
    {
        // TODO: allow for zero copy training
        unimplemented!()
    }

    // TODO: make more type safe; should be using a Path instead of a &str
    pub fn load_model(path: &str) -> Result<Model<'a>, ModelCreationError> {
        let model = unsafe {
            libsvm_sys::svm_load_model(path.as_ptr() as *const libc::c_char)
        };

        if model.is_null() {
            Err(ModelCreationError::UnableToLoadModel)
        } else {
            Ok(Model {
                inner: model,
                data: None,
            })
        }
    }

    pub fn save_model(&self, path: &str) -> Result<(), ModelSaveError> {
        let ret = unsafe {
            libsvm_sys::svm_save_model(
                path.as_ptr() as *const libc::c_char,
                self.inner as *const libsvm_sys::SvmModel)
        };

        if ret < 0 {
            Err(ModelSaveError)
        } else {
            Ok(())
        }
    }

    pub fn svm_type(&self) -> Option<SvmType> {
        let ret = unsafe {
            libsvm_sys::svm_get_svm_type(self.inner as *const libsvm_sys::SvmModel)
        };
        SvmType::from_integral(ret)
    }

    pub fn nr_class(&self) -> libc::c_int {
        unsafe {
            libsvm_sys::svm_get_nr_class(self.inner as *const libsvm_sys::SvmModel)
        }
    }

    // TODO: provide type safe wrappers for labels and sv_indices
    // need a way to see if the model has labels or sv_indices
    pub fn labels(&self) -> Vec<libc::c_int> {
        unimplemented!()
    }

    pub fn sv_indices(&self) -> Vec<libc::c_int> {
        unimplemented!()
    }

    pub fn predict(&self, data: &[f64]) -> libc::c_double {
        let sparse = SparseVector::from(data);
        unsafe {
            libsvm_sys::svm_predict(
                self.inner as *const libsvm_sys::SvmModel,
                sparse.as_ptr()
            )
        }
    }

    pub fn predict_sparse(&self, data: &SparseVector) -> libc::c_double {
        unimplemented!()
    }
}

impl <'a> Drop for Model<'a> {
    fn drop(&mut self) {
        let mut ptr = self.inner as *mut libsvm_sys::SvmModel;
        unsafe {
            libsvm_sys::svm_free_and_destroy_model(&mut ptr as *mut *mut libsvm_sys::SvmModel);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        let testx = vec![
            ([0.0, 0.0f64], -1),
            ([0.0, 1.0], -1),
            ([1.0, 0.0], 1),
            ([1.0, 1.0], 1),
        ];
        let parameters = Parameters::new()
            .c_svc(1.0, &[], &[]);
        let model = Model::train(&testx, &parameters).unwrap();
        let prediction = model.predict(&[0.0, -1.0]);
        assert_eq!(prediction, -1.0);
    }
}
<|endoftext|>


<commit_before>#![cfg(feature = "tensorflow_unstable")]

extern crate libc;
extern crate libtensorflow_sys;

use std::ffi::CStr;
use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use std::ops::Drop;

use libtensorflow_sys as tf;

mod buffer;
pub use buffer::Buffer;

////////////////////////

fn check_not_null<T>(p: *mut T) -> *mut T {
  assert!(!p.is_null());
  p
}

////////////////////////

macro_rules! impl_new {
  ($name: ident, $call:ident) => {
    impl $name {
      pub fn new() -> Self {
        unsafe {
          $name {
            inner: check_not_null(tf::$call()),
          }
        }
      }
    }
  }
}

////////////////////////

macro_rules! impl_drop {
  ($name: ident, $call:ident) => {
    impl Drop for $name {
      fn drop(&mut self) {
        unsafe {
          tf::$call(self.inner);
        }
      }
    }
  }
}

////////////////////////

macro_rules! c_enum {
  ($enum_name:ident { $($name:ident = $num:expr),* }) => {
    #[derive(PartialEq,Eq,PartialOrd,Ord,Debug)]
    pub enum $enum_name {
      UnrecognizedEnumValue(::libc::c_uint),
      $($name),*
    }

    impl $enum_name {
      #[allow(dead_code)]
      fn from_int(value: ::libc::c_uint) -> $enum_name {
        match value {
          $($num => $enum_name::$name,)*
          c => $enum_name::UnrecognizedEnumValue(c),
        }
      }

      #[allow(dead_code)]
      fn to_int(&self) -> ::libc::c_uint {
        match self {
          &$enum_name::UnrecognizedEnumValue(c) => c,
          $(&$enum_name::$name => $num),*
        }
      }
    }

    impl ::std::fmt::Display for $enum_name {
      fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match self {
          $(&$enum_name::$name => f.write_str(stringify!($name)),)*
          &$enum_name::UnrecognizedEnumValue(c) => write!(f, "UnrecognizedEnumValue({})", c),
        }
      }
    }
  };
  ($enum_name:ident { $($name:ident = $num:expr,)* }) => {
    c_enum!($enum_name { $($name = $num),* });
  }
}

////////////////////////

c_enum!(Code {
  Ok = 0,
  Cancelled = 1,
  Unknown = 2,
  InvalidArgument = 3,
  DeadlineExceeded = 4,
  NotFound = 5,
  AlreadyExists = 6,
  PermissionDenied = 7,
  ResourceExhausted = 8,
  FailedPrecondition = 9,
  Aborted = 10,
  OutOfRange = 11,
  Unimplemented = 12,
  Internal = 13,
  Unavailable = 14,
  DataLoss = 15,
  Unauthenticated = 16,
});

////////////////////////

c_enum!(DataType {
  Float = 1,
  Double = 2,
  Int32 = 3,
  UInt8 = 4,
  Int16 = 5,
  Int8 = 6,
  String = 7,
  Complex = 8,
  Int64 = 9,
  Bool = 10,
  QInt8 = 11,
  QUInt8 = 12,
  QInt32 = 13,
  BFloat16 = 14,
  QInt16 = 15,
  QUInt16 = 16,
});

////////////////////////

pub struct Status {
  inner: *mut tf::TF_Status,
}

impl_new!(Status, TF_NewStatus);
impl_drop!(Status, TF_DeleteStatus);

impl Status {
  pub fn code(&self) -> Code {
    unsafe {
      Code::from_int(tf::TF_GetCode(self.inner))
    }
  }

  pub fn is_ok(&self) -> bool {
    self.code() == Code::Ok
  }
}

impl Display for Status {
  fn fmt(&self, f: &mut Formatter) -> fmt::Result {
    unsafe {
      try!(write!(f, "{}: ", self.code()));
      let msg = match CStr::from_ptr(tf::TF_Message(self.inner)).to_str() {
        Ok(s) => s,
        Err(_) => "<invalid UTF-8 in message>",
      };
      f.write_str(msg)
    }
  }
}

////////////////////////

pub struct SessionOptions {
  inner: *mut tf::TF_SessionOptions,
}

impl_new!(SessionOptions, TF_NewSessionOptions);
impl_drop!(SessionOptions, TF_DeleteSessionOptions);

////////////////////////

pub struct Session {
  inner: *mut tf::TF_Session,
}

impl Session {
  pub fn new(options: &SessionOptions) -> Result<Self> {
    let status = Status::new();
    let inner = unsafe { tf::TF_NewSession(options.inner, status.inner) };
    if inner.is_null() {
      Err(status)
    } else {
      Ok(Session {
        inner: inner,
      })
    }
  }

  pub fn close(&mut self) -> Status {
    let status = Status::new();
    unsafe {
      tf::TF_CloseSession(self.inner, status.inner);
    }
    status
  }
}

impl Drop for Session {
  fn drop(&mut self) {
    let status = Status::new();
    unsafe {
      tf::TF_DeleteSession(self.inner, status.inner);
    }
    // TODO: What do we do with the status?
  }
}

////////////////////////

pub type Result<T> = std::result::Result<T, Status>;

////////////////////////

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

  fn create_session() -> Session {
    let options = SessionOptions::new();
    match Session::new(&options) {
      Ok(session) => session,
      Err(status) => panic!("Creating session failed with status: {}", status),
    }
  }

  #[test]
  fn smoke() {
    create_session();
  }

  #[test]
  fn test_close() {
    let status = create_session().close();
    assert!(status.is_ok());
  }
}
<commit_msg>Add Tensor type<commit_after>#![cfg(feature = "tensorflow_unstable")]

extern crate libc;
extern crate libtensorflow_sys;

use std::ffi::CStr;
use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use std::ops::Drop;

use libtensorflow_sys as tf;

mod buffer;
pub use buffer::Buffer;

////////////////////////

fn check_not_null<T>(p: *mut T) -> *mut T {
  assert!(!p.is_null());
  p
}

////////////////////////

macro_rules! impl_new {
  ($name: ident, $call:ident) => {
    impl $name {
      pub fn new() -> Self {
        unsafe {
          $name {
            inner: check_not_null(tf::$call()),
          }
        }
      }
    }
  }
}

////////////////////////

macro_rules! impl_drop {
  ($name: ident, $call:ident) => {
    impl Drop for $name {
      fn drop(&mut self) {
        unsafe {
          tf::$call(self.inner);
        }
      }
    }
  }
}

////////////////////////

macro_rules! c_enum {
  ($enum_name:ident { $($name:ident = $num:expr),* }) => {
    #[derive(PartialEq,Eq,PartialOrd,Ord,Debug)]
    pub enum $enum_name {
      UnrecognizedEnumValue(::libc::c_uint),
      $($name),*
    }

    impl $enum_name {
      #[allow(dead_code)]
      fn from_int(value: ::libc::c_uint) -> $enum_name {
        match value {
          $($num => $enum_name::$name,)*
          c => $enum_name::UnrecognizedEnumValue(c),
        }
      }

      #[allow(dead_code)]
      fn to_int(&self) -> ::libc::c_uint {
        match self {
          &$enum_name::UnrecognizedEnumValue(c) => c,
          $(&$enum_name::$name => $num),*
        }
      }
    }

    impl ::std::fmt::Display for $enum_name {
      fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match self {
          $(&$enum_name::$name => f.write_str(stringify!($name)),)*
          &$enum_name::UnrecognizedEnumValue(c) => write!(f, "UnrecognizedEnumValue({})", c),
        }
      }
    }
  };
  ($enum_name:ident { $($name:ident = $num:expr,)* }) => {
    c_enum!($enum_name { $($name = $num),* });
  }
}

////////////////////////

c_enum!(Code {
  Ok = 0,
  Cancelled = 1,
  Unknown = 2,
  InvalidArgument = 3,
  DeadlineExceeded = 4,
  NotFound = 5,
  AlreadyExists = 6,
  PermissionDenied = 7,
  ResourceExhausted = 8,
  FailedPrecondition = 9,
  Aborted = 10,
  OutOfRange = 11,
  Unimplemented = 12,
  Internal = 13,
  Unavailable = 14,
  DataLoss = 15,
  Unauthenticated = 16,
});

////////////////////////

c_enum!(DataType {
  Float = 1,
  Double = 2,
  Int32 = 3,
  UInt8 = 4,
  Int16 = 5,
  Int8 = 6,
  String = 7,
  Complex = 8,
  Int64 = 9,
  Bool = 10,
  QInt8 = 11,
  QUInt8 = 12,
  QInt32 = 13,
  BFloat16 = 14,
  QInt16 = 15,
  QUInt16 = 16,
});

////////////////////////

pub struct Status {
  inner: *mut tf::TF_Status,
}

impl_new!(Status, TF_NewStatus);
impl_drop!(Status, TF_DeleteStatus);

impl Status {
  pub fn code(&self) -> Code {
    unsafe {
      Code::from_int(tf::TF_GetCode(self.inner))
    }
  }

  pub fn is_ok(&self) -> bool {
    self.code() == Code::Ok
  }
}

impl Display for Status {
  fn fmt(&self, f: &mut Formatter) -> fmt::Result {
    unsafe {
      try!(write!(f, "{}: ", self.code()));
      let msg = match CStr::from_ptr(tf::TF_Message(self.inner)).to_str() {
        Ok(s) => s,
        Err(_) => "<invalid UTF-8 in message>",
      };
      f.write_str(msg)
    }
  }
}

////////////////////////

pub struct SessionOptions {
  inner: *mut tf::TF_SessionOptions,
}

impl_new!(SessionOptions, TF_NewSessionOptions);
impl_drop!(SessionOptions, TF_DeleteSessionOptions);

////////////////////////

pub struct Session {
  inner: *mut tf::TF_Session,
}

impl Session {
  pub fn new(options: &SessionOptions) -> Result<Self> {
    let status = Status::new();
    let inner = unsafe { tf::TF_NewSession(options.inner, status.inner) };
    if inner.is_null() {
      Err(status)
    } else {
      Ok(Session {
        inner: inner,
      })
    }
  }

  pub fn close(&mut self) -> Status {
    let status = Status::new();
    unsafe {
      tf::TF_CloseSession(self.inner, status.inner);
    }
    status
  }
}

impl Drop for Session {
  fn drop(&mut self) {
    let status = Status::new();
    unsafe {
      tf::TF_DeleteSession(self.inner, status.inner);
    }
    // TODO: What do we do with the status?
  }
}

////////////////////////

pub type Result<T> = std::result::Result<T, Status>;

////////////////////////

trait TensorType: Default + Clone {
  // TODO: Use associated constants when/if available
  fn data_type() -> DataType;
}

macro_rules! tensor_type {
  ($rust_type:ident, $tensor_type:ident) => {
    impl TensorType for $rust_type {
      fn data_type() -> DataType {
        DataType::$tensor_type
      }
    }
  }
}

tensor_type!(f32, Float);
tensor_type!(f64, Double);
tensor_type!(i32, Int32);
tensor_type!(u8, UInt8);
tensor_type!(i16, Int16);
tensor_type!(i8, Int8);
// TODO: provide type for String
// TODO: provide type for Complex
tensor_type!(i64, Int64);
tensor_type!(bool, Bool);
// TODO: provide type for QInt8
// TODO: provide type for QUInt8
// TODO: provide type for QInt32
// TODO: provide type for BFloat16
// TODO: provide type for QInt16
// TODO: provide type for QUInt16

////////////////////////

pub struct Tensor<T> {
  inner: *mut tf::TF_Tensor,
  data: Buffer<T>,
  dims: Vec<u64>,
}

extern "C" fn noop_deallocator(_data: *mut ::libc::c_void,
                               _len: ::libc::size_t,
                               _arg: *mut ::libc::c_void)-> () {
}

// TODO: Replace with Iterator::product once that's stable
fn product(values: &[u64]) -> u64 {
  let mut product = 1;
  for v in values.iter() {
    product *= *v;
  }
  product
}

impl<T: TensorType> Tensor<T> {
  pub fn new(dims: &[u64]) -> Self {
    let total = product(dims);
    let data = <Buffer<T>>::new(total as usize);
    // Guaranteed safe to unwrap, because the only way for it to fail is for the
    // length of the buffer not to match the dimensions, and we created it with
    // exactly the right size.
    Self::new_with_buffer(dims, data).unwrap()
  }

  pub fn new_with_buffer(dims: &[u64], data: Buffer<T>) -> Option<Self> {
    let total = product(dims);
    if total != data.len() as u64 {
      return None
    }
    let inner = unsafe {
      tf::TF_NewTensor(T::data_type().to_int(),
                       dims.as_ptr() as *mut i64,
                       dims.len() as i32,
                       data.as_ptr() as *mut libc::c_void,
                       data.len(),
                       Some(noop_deallocator),
                       std::ptr::null_mut())
    };
    let mut dims_vec = Vec::new();
    // TODO: Use extend_from_slice once we're on Rust 1.6
    dims_vec.extend(dims.iter());
    Some(Tensor {
      inner: inner,
      data: data,
      dims: dims_vec,
    })
  }

  pub fn data(&self) -> &Buffer<T> {
    &self.data
  }

  pub fn data_mut(&mut self) -> &mut Buffer<T> {
    &mut self.data
  }

  pub fn dims(&self) -> &[u64] {
    &self.dims
  }
}

impl<T> Drop for Tensor<T> {
  fn drop(&mut self) {
    unsafe {
      tf::TF_DeleteTensor(self.inner);
    }
  }
}

////////////////////////

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

  fn create_session() -> Session {
    let options = SessionOptions::new();
    match Session::new(&options) {
      Ok(session) => session,
      Err(status) => panic!("Creating session failed with status: {}", status),
    }
  }

  #[test]
  fn smoke() {
    create_session();
  }

  #[test]
  fn test_close() {
    let status = create_session().close();
    assert!(status.is_ok());
  }

  #[test]
  fn test_tensor() {
    let mut tensor = <Tensor<f32>>::new(&[2, 3]);
    assert_eq!(tensor.data().len(), 6);
    tensor.data_mut()[0] = 1.0;
  }
}
<|endoftext|>


<commit_before>//!Log tools.

use std::io::{self, Write};
use std::fs;
use std::sync::Mutex;

pub type Result = io::Result<()>;

///Common trait for log tools.
pub trait Log {
	///Print a note to the log or return eventual errors.
	fn try_note(&self, message: &str) -> Result;
	///Print a warning to the log or return eventual errors.
	fn try_warning(&self, message: &str) -> Result;
	///Print an error to the log or return eventual errors.
	fn try_error(&self, message: &str) -> Result;

	///Print a note to the log and ignore any errors.
	#[allow(unused_must_use)]
	#[inline]
	fn note(&self, message: &str) {
		self.try_note(message);
	}
	///Print a warning to the log and ignore any errors.
	#[allow(unused_must_use)]
	#[inline]
	fn warning(&self, message: &str) {
		self.try_warning(message);
	}
	///Print an error to the log and ignore any errors.
	#[allow(unused_must_use)]
	#[inline]
	fn error(&self, message: &str) {
		self.try_error(message);
	}
}

///A quiet log tool. Nothing will be printed anywhere.
pub struct Quiet;

impl Log for Quiet {
	fn try_note(&self, _message: &str) -> Result {
		Ok(())
	}

	fn try_warning(&self, _message: &str) -> Result {
		Ok(())
	}

	fn try_error(&self, _message: &str) -> Result {
		Ok(())
	}
}

///Log tool for printing to standard output.
pub struct StdOut;

impl Log for StdOut {
	fn try_note(&self, message: &str) -> Result {
		println!("note: {}", message);
		Ok(())
	}

	fn try_warning(&self, message: &str) -> Result {
		println!("warning: {}", message);
		Ok(())
	}

	fn try_error(&self, message: &str) -> Result {
		println!("error: {}", message);
		Ok(())
	}
}

///Log tool for printing to a file.
pub struct File {
	file: Mutex<fs::File>
}

impl File {
	pub fn new(file: fs::File) -> File {
		File {
			file: Mutex::new(file)
		}
	}
}

impl Log for File {
	fn try_note(&self, message: &str) -> Result {
		let mut f = match self.file.lock() {
			Ok(f) => f,
			Err(_e) => return Err(io::Error::new(io::ErrorKind::Other, "poisoned log file lock"))
		};
		write!(f, "note: {}", message)
	}

	fn try_warning(&self, message: &str) -> Result {
		let mut f = match self.file.lock() {
			Ok(f) => f,
			Err(_e) => return Err(io::Error::new(io::ErrorKind::Other, "poisoned log file lock"))
		};
		write!(f, "warning: {}", message)
	}

	fn try_error(&self, message: &str) -> Result {
		let mut f = match self.file.lock() {
			Ok(f) => f,
			Err(_e) => return Err(io::Error::new(io::ErrorKind::Other, "poisoned log file lock"))
		};
		write!(f, "error: {}", message)
	}
}

#[cfg(test)]
mod test {
	use std::fs;
	use log;
	use Server;
	use Context;
	use Response;
	use tempdir;

	fn handler(_c: Context, _w: Response) {}

	#[test]
	fn log_to_file() {
		let dir = tempdir::TempDir::new("log_to_file").unwrap();
		let file = fs::File::create(&dir.path().join("test.log")).unwrap();
		Server {
			log: Box::new(log::File::new(file)),
			..Server::new(handler)
		}.build();
	}
}<commit_msg>Reindent log.rs using spaces<commit_after>//!Log tools.

use std::io::{self, Write};
use std::fs;
use std::sync::Mutex;

pub type Result = io::Result<()>;

///Common trait for log tools.
pub trait Log {
    ///Print a note to the log or return eventual errors.
    fn try_note(&self, message: &str) -> Result;
    ///Print a warning to the log or return eventual errors.
    fn try_warning(&self, message: &str) -> Result;
    ///Print an error to the log or return eventual errors.
    fn try_error(&self, message: &str) -> Result;

    ///Print a note to the log and ignore any errors.
    #[allow(unused_must_use)]
    #[inline]
    fn note(&self, message: &str) {
        self.try_note(message);
    }
    ///Print a warning to the log and ignore any errors.
    #[allow(unused_must_use)]
    #[inline]
    fn warning(&self, message: &str) {
        self.try_warning(message);
    }
    ///Print an error to the log and ignore any errors.
    #[allow(unused_must_use)]
    #[inline]
    fn error(&self, message: &str) {
        self.try_error(message);
    }
}

///A quiet log tool. Nothing will be printed anywhere.
pub struct Quiet;

impl Log for Quiet {
    fn try_note(&self, _message: &str) -> Result {
        Ok(())
    }

    fn try_warning(&self, _message: &str) -> Result {
        Ok(())
    }

    fn try_error(&self, _message: &str) -> Result {
        Ok(())
    }
}

///Log tool for printing to standard output.
pub struct StdOut;

impl Log for StdOut {
    fn try_note(&self, message: &str) -> Result {
        println!("note: {}", message);
        Ok(())
    }

    fn try_warning(&self, message: &str) -> Result {
        println!("warning: {}", message);
        Ok(())
    }

    fn try_error(&self, message: &str) -> Result {
        println!("error: {}", message);
        Ok(())
    }
}

///Log tool for printing to a file.
pub struct File {
    file: Mutex<fs::File>
}

impl File {
    pub fn new(file: fs::File) -> File {
        File {
            file: Mutex::new(file)
        }
    }
}

impl Log for File {
    fn try_note(&self, message: &str) -> Result {
        let mut f = match self.file.lock() {
            Ok(f) => f,
            Err(_e) => return Err(io::Error::new(io::ErrorKind::Other, "poisoned log file lock"))
        };
        write!(f, "note: {}", message)
    }

    fn try_warning(&self, message: &str) -> Result {
        let mut f = match self.file.lock() {
            Ok(f) => f,
            Err(_e) => return Err(io::Error::new(io::ErrorKind::Other, "poisoned log file lock"))
        };
        write!(f, "warning: {}", message)
    }

    fn try_error(&self, message: &str) -> Result {
        let mut f = match self.file.lock() {
            Ok(f) => f,
            Err(_e) => return Err(io::Error::new(io::ErrorKind::Other, "poisoned log file lock"))
        };
        write!(f, "error: {}", message)
    }
}

#[cfg(test)]
mod test {
    use std::fs;
    use log;
    use Server;
    use Context;
    use Response;
    use tempdir;

    fn handler(_c: Context, _w: Response) {}

    #[test]
    fn log_to_file() {
        let dir = tempdir::TempDir::new("log_to_file").unwrap();
        let file = fs::File::create(&dir.path().join("test.log")).unwrap();
        Server {
            log: Box::new(log::File::new(file)),
            ..Server::new(handler)
        }.build();
    }
}<|endoftext|>


<commit_before>#![allow(unsafe_code)]

use std::{
    borrow::{Borrow, BorrowMut},
    convert::TryFrom,
    hash::{Hash, Hasher},
    mem::MaybeUninit,
    sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
};

use crate::{
    atomic_shim::AtomicU64,
    debug_delay,
    dll::{DoublyLinkedList, Node},
    fastlock::FastLock,
    FastSet8, Guard, PageId,
};

#[cfg(any(test, feature = "lock_free_delays"))]
const MAX_QUEUE_ITEMS: usize = 4;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const MAX_QUEUE_ITEMS: usize = 64;

#[cfg(any(test, feature = "lock_free_delays"))]
const N_SHARDS: usize = 2;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const N_SHARDS: usize = 256;

struct AccessBlock {
    len: AtomicUsize,
    block: [AtomicU64; MAX_QUEUE_ITEMS],
    next: AtomicPtr<AccessBlock>,
}

impl Default for AccessBlock {
    fn default() -> AccessBlock {
        AccessBlock {
            len: AtomicUsize::new(0),
            block: unsafe { MaybeUninit::zeroed().assume_init() },
            next: AtomicPtr::default(),
        }
    }
}

struct AccessQueue {
    writing: AtomicPtr<AccessBlock>,
    full_list: AtomicPtr<AccessBlock>,
}

impl AccessBlock {
    fn new(item: CacheAccess) -> AccessBlock {
        let mut ret = AccessBlock {
            len: AtomicUsize::new(1),
            block: unsafe { MaybeUninit::zeroed().assume_init() },
            next: AtomicPtr::default(),
        };
        ret.block[0] = AtomicU64::from(u64::from(item));
        ret
    }
}

impl Default for AccessQueue {
    fn default() -> AccessQueue {
        AccessQueue {
            writing: AtomicPtr::new(Box::into_raw(Box::new(
                AccessBlock::default(),
            ))),
            full_list: AtomicPtr::default(),
        }
    }
}

impl AccessQueue {
    fn push(&self, item: CacheAccess) -> bool {
        loop {
            debug_delay();
            let head = self.writing.load(Ordering::Acquire);
            let block = unsafe { &*head };

            debug_delay();
            let offset = block.len.fetch_add(1, Ordering::Acquire);

            if offset < MAX_QUEUE_ITEMS {
                let item_u64: u64 = item.into();
                assert_ne!(item_u64, 0);
                debug_delay();
                unsafe {
                    block
                        .block
                        .get_unchecked(offset)
                        .store(item_u64, Ordering::Release);
                }
                return false;
            } else {
                // install new writer
                let new = Box::into_raw(Box::new(AccessBlock::new(item)));
                debug_delay();
                let res = self.writing.compare_exchange_weak(
                    head,
                    new,
                    Ordering::Release,
                    Ordering::Relaxed,
                );
                if res.is_err() {
                    // we lost the CAS, free the new item that was
                    // never published to other threads
                    unsafe {
                        drop(Box::from_raw(new));
                    }
                    continue;
                }

                // push the now-full item to the full list for future
                // consumption
                let mut ret;
                let mut full_list_ptr = self.full_list.load(Ordering::Acquire);
                while {
                    // we loop because maybe other threads are pushing stuff too
                    block.next.store(full_list_ptr, Ordering::Release);
                    debug_delay();
                    ret = self.full_list.compare_exchange_weak(
                        full_list_ptr,
                        head,
                        Ordering::Release,
                        Ordering::Relaxed,
                    );
                    ret.is_err()
                } {
                    full_list_ptr = ret.unwrap_err();
                }
                return true;
            }
        }
    }

    fn take<'a>(&self, guard: &'a Guard) -> CacheAccessIter<'a> {
        debug_delay();
        let ptr = self.full_list.swap(std::ptr::null_mut(), Ordering::AcqRel);

        CacheAccessIter { guard, current_offset: 0, current_block: ptr }
    }
}

impl Drop for AccessQueue {
    fn drop(&mut self) {
        debug_delay();
        let writing = self.writing.load(Ordering::Acquire);
        unsafe {
            Box::from_raw(writing);
        }
        debug_delay();
        let mut head = self.full_list.load(Ordering::Acquire);
        while !head.is_null() {
            unsafe {
                debug_delay();
                let next =
                    (*head).next.swap(std::ptr::null_mut(), Ordering::Release);
                Box::from_raw(head);
                head = next;
            }
        }
    }
}

struct CacheAccessIter<'a> {
    guard: &'a Guard,
    current_offset: usize,
    current_block: *mut AccessBlock,
}

impl<'a> Iterator for CacheAccessIter<'a> {
    type Item = CacheAccess;

    fn next(&mut self) -> Option<CacheAccess> {
        while !self.current_block.is_null() {
            let current_block = unsafe { &*self.current_block };

            debug_delay();
            if self.current_offset >= MAX_QUEUE_ITEMS {
                let to_drop_ptr = self.current_block;
                debug_delay();
                self.current_block = current_block.next.load(Ordering::Acquire);
                self.current_offset = 0;
                debug_delay();
                let to_drop = unsafe { Box::from_raw(to_drop_ptr) };
                self.guard.defer(|| to_drop);
                continue;
            }

            let mut next = 0;
            while next == 0 {
                // we spin here because there's a race between bumping
                // the offset and setting the value to something other
                // than 0 (and 0 is an invalid value)
                debug_delay();
                next = current_block.block[self.current_offset]
                    .load(Ordering::Acquire);
            }
            self.current_offset += 1;
            return Some(CacheAccess::from(next));
        }

        None
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct CacheAccess {
    // safe because MAX_PID_BITS / N_SHARDS < u32::MAX
    //                     2**37 /   2**8   < 2**32
    pub pid: u32,
    pub sz: u8,
}

impl From<CacheAccess> for u64 {
    fn from(ca: CacheAccess) -> u64 {
        (u64::from(ca.pid) << 8) | u64::from(ca.sz)
    }
}

#[allow(clippy::fallible_impl_from)]
impl From<u64> for CacheAccess {
    fn from(u: u64) -> CacheAccess {
        let sz = usize::try_from((u << 56) >> 56).unwrap();
        assert_ne!(sz, 0);
        let pid = u >> 8;
        assert!(pid < u64::from(u32::MAX));
        CacheAccess {
            pid: u32::try_from(pid).unwrap(),
            sz: u8::try_from(sz).unwrap(),
        }
    }
}

impl CacheAccess {
    fn size(&self) -> usize {
        1 << usize::from(self.sz)
    }

    fn new(pid: PageId, sz: usize) -> CacheAccess {
        let rounded_up_power_of_2 =
            u8::try_from(sz.next_power_of_two().trailing_zeros()).unwrap();

        CacheAccess {
            pid: u32::try_from(pid).expect("expected caller to shift pid down"),
            sz: rounded_up_power_of_2,
        }
    }
}

/// A simple LRU cache.
pub struct Lru {
    shards: Vec<(AccessQueue, FastLock<Shard>)>,
}

impl Lru {
    /// Instantiates a new `Lru` cache.
    pub(crate) fn new(cache_capacity: usize) -> Self {
        assert!(
            cache_capacity >= N_SHARDS,
            "Please configure the cache \
             capacity to be at least 256 bytes"
        );
        let shard_capacity = cache_capacity / N_SHARDS;

        let mut shards = Vec::with_capacity(N_SHARDS);
        shards.resize_with(N_SHARDS, || {
            (AccessQueue::default(), FastLock::new(Shard::new(shard_capacity)))
        });

        Self { shards }
    }

    /// Called when an item is accessed. Returns a Vec of items to be
    /// evicted. Uses flat-combining to avoid blocking on what can
    /// be an asynchronous operation.
    ///
    /// layout:
    ///   items:   1 2 3 4 5 6 7 8 9 10
    ///   shards:  1 0 1 0 1 0 1 0 1 0
    ///   shard 0:   2   4   6   8   10
    ///   shard 1: 1   3   5   7   9
    pub(crate) fn accessed(
        &self,
        id: PageId,
        item_size: usize,
        guard: &Guard,
    ) -> Vec<PageId> {
        const SHARD_BITS: usize = N_SHARDS.trailing_zeros() as usize;

        let mut ret = vec![];
        let shards = N_SHARDS as u64;
        let (shard_idx, shifted_pid) = (id % shards, id >> SHARD_BITS);
        let (access_queue, shard_mu) = &self.shards[safe_usize(shard_idx)];

        let cache_access = CacheAccess::new(shifted_pid, item_size);
        let filled = access_queue.push(cache_access);

        if filled {
            // only try to acquire this if the access queue has filled
            // an entire segment
            if let Some(mut shard) = shard_mu.try_lock() {
                let accesses = access_queue.take(guard);
                for item in accesses {
                    let to_evict = shard.accessed(item);
                    // map shard internal offsets to global items ids
                    for pos in to_evict {
                        let item =
                            (PageId::from(pos) << SHARD_BITS) + shard_idx;
                        ret.push(item);
                    }
                }
            }
        }
        ret
    }
}

#[derive(Eq)]
struct Entry(*mut Node);

unsafe impl Send for Entry {}

impl Ord for Entry {
    fn cmp(&self, other: &Entry) -> std::cmp::Ordering {
        let left_pid: u32 = *self.borrow();
        let right_pid: u32 = *other.borrow();
        left_pid.cmp(&right_pid)
    }
}

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

impl PartialEq for Entry {
    fn eq(&self, other: &Entry) -> bool {
        unsafe { (*self.0).pid == (*other.0).pid }
    }
}

impl BorrowMut<CacheAccess> for Entry {
    fn borrow_mut(&mut self) -> &mut CacheAccess {
        unsafe { &mut *self.0 }
    }
}

impl Borrow<CacheAccess> for Entry {
    fn borrow(&self) -> &CacheAccess {
        unsafe { &*self.0 }
    }
}

impl Borrow<u32> for Entry {
    fn borrow(&self) -> &u32 {
        unsafe { &(*self.0).pid }
    }
}

// we only hash on pid, since we will change
// sz sometimes and we access the item by pid
impl Hash for Entry {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        unsafe { (*self.0).pid.hash(hasher) }
    }
}

struct Shard {
    dll: DoublyLinkedList,
    entries: FastSet8<Entry>,
    capacity: usize,
    size: usize,
}

impl Shard {
    fn new(capacity: usize) -> Self {
        assert!(capacity > 0, "shard capacity must be non-zero");

        Self {
            dll: DoublyLinkedList::default(),
            entries: FastSet8::default(),
            capacity,
            size: 0,
        }
    }

    /// `PageId`s in the shard list are indexes of the entries.
    fn accessed(&mut self, cache_access: CacheAccess) -> Vec<u32> {
        if let Some(entry) = self.entries.get(&cache_access.pid) {
            let old_sz_po2 = unsafe { (*entry.0).swap_sz(cache_access.sz) };
            let old_size = 1 << usize::from(old_sz_po2);

            self.size -= old_size;
            self.dll.promote(entry.0);
        } else {
            let ptr = self.dll.push_head(cache_access);
            self.entries.insert(Entry(ptr));
        };

        self.size += cache_access.size();

        let mut to_evict = vec![];

        while self.size > self.capacity {
            if self.dll.len() == 1 {
                // don't evict what we just added
                break;
            }

            let node = self.dll.pop_tail().unwrap();

            self.entries.remove(&node.pid);

            to_evict.push(node.pid);

            self.size -= node.size();

            // NB: node is stored in our entries map
            // via a raw pointer, which points to
            // the same allocation used in the DLL.
            // We have to be careful to free node
            // only after removing it from both
            // the DLL and our entries map.
            drop(node);
        }

        to_evict
    }
}

#[inline]
fn safe_usize(value: PageId) -> usize {
    usize::try_from(value).unwrap()
}

#[test]
fn lru_smoke_test() {
    use crate::pin;

    let lru = Lru::new(2);
    for i in 0..1000 {
        let guard = pin();
        lru.accessed(i, 16, &guard);
    }
}

#[test]
fn lru_access_test() {
    use crate::pin;

    let ci = CacheAccess::new(6, 20667);
    assert_eq!(ci.size(), 32 * 1024);

    let lru = Lru::new(4096);

    let guard = pin();

    assert_eq!(lru.accessed(0, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(2, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(4, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(6, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(8, 20667, &guard), vec![0, 2, 4]);
    assert_eq!(lru.accessed(10, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(12, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(14, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(16, 20667, &guard), vec![6, 8, 10, 12]);
    assert_eq!(lru.accessed(18, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(20, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(22, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(24, 20667, &guard), vec![14, 16, 18, 20]);
}
<commit_msg>Add correctness assertion in LRU<commit_after>#![allow(unsafe_code)]

use std::{
    borrow::{Borrow, BorrowMut},
    convert::TryFrom,
    hash::{Hash, Hasher},
    mem::MaybeUninit,
    sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
};

use crate::{
    atomic_shim::AtomicU64,
    debug_delay,
    dll::{DoublyLinkedList, Node},
    fastlock::FastLock,
    FastSet8, Guard, PageId,
};

#[cfg(any(test, feature = "lock_free_delays"))]
const MAX_QUEUE_ITEMS: usize = 4;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const MAX_QUEUE_ITEMS: usize = 64;

#[cfg(any(test, feature = "lock_free_delays"))]
const N_SHARDS: usize = 2;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const N_SHARDS: usize = 256;

struct AccessBlock {
    len: AtomicUsize,
    block: [AtomicU64; MAX_QUEUE_ITEMS],
    next: AtomicPtr<AccessBlock>,
}

impl Default for AccessBlock {
    fn default() -> AccessBlock {
        AccessBlock {
            len: AtomicUsize::new(0),
            block: unsafe { MaybeUninit::zeroed().assume_init() },
            next: AtomicPtr::default(),
        }
    }
}

struct AccessQueue {
    writing: AtomicPtr<AccessBlock>,
    full_list: AtomicPtr<AccessBlock>,
}

impl AccessBlock {
    fn new(item: CacheAccess) -> AccessBlock {
        let mut ret = AccessBlock {
            len: AtomicUsize::new(1),
            block: unsafe { MaybeUninit::zeroed().assume_init() },
            next: AtomicPtr::default(),
        };
        ret.block[0] = AtomicU64::from(u64::from(item));
        ret
    }
}

impl Default for AccessQueue {
    fn default() -> AccessQueue {
        AccessQueue {
            writing: AtomicPtr::new(Box::into_raw(Box::new(
                AccessBlock::default(),
            ))),
            full_list: AtomicPtr::default(),
        }
    }
}

impl AccessQueue {
    fn push(&self, item: CacheAccess) -> bool {
        loop {
            debug_delay();
            let head = self.writing.load(Ordering::Acquire);
            let block = unsafe { &*head };

            debug_delay();
            let offset = block.len.fetch_add(1, Ordering::Acquire);

            if offset < MAX_QUEUE_ITEMS {
                let item_u64: u64 = item.into();
                assert_ne!(item_u64, 0);
                debug_delay();
                unsafe {
                    block
                        .block
                        .get_unchecked(offset)
                        .store(item_u64, Ordering::Release);
                }
                return false;
            } else {
                // install new writer
                let new = Box::into_raw(Box::new(AccessBlock::new(item)));
                debug_delay();
                let res = self.writing.compare_exchange_weak(
                    head,
                    new,
                    Ordering::Release,
                    Ordering::Relaxed,
                );
                if res.is_err() {
                    // we lost the CAS, free the new item that was
                    // never published to other threads
                    unsafe {
                        drop(Box::from_raw(new));
                    }
                    continue;
                }

                // push the now-full item to the full list for future
                // consumption
                let mut ret;
                let mut full_list_ptr = self.full_list.load(Ordering::Acquire);
                while {
                    // we loop because maybe other threads are pushing stuff too
                    block.next.store(full_list_ptr, Ordering::Release);
                    debug_delay();
                    ret = self.full_list.compare_exchange_weak(
                        full_list_ptr,
                        head,
                        Ordering::Release,
                        Ordering::Relaxed,
                    );
                    ret.is_err()
                } {
                    full_list_ptr = ret.unwrap_err();
                }
                return true;
            }
        }
    }

    fn take<'a>(&self, guard: &'a Guard) -> CacheAccessIter<'a> {
        debug_delay();
        let ptr = self.full_list.swap(std::ptr::null_mut(), Ordering::AcqRel);

        CacheAccessIter { guard, current_offset: 0, current_block: ptr }
    }
}

impl Drop for AccessQueue {
    fn drop(&mut self) {
        debug_delay();
        let writing = self.writing.load(Ordering::Acquire);
        unsafe {
            Box::from_raw(writing);
        }
        debug_delay();
        let mut head = self.full_list.load(Ordering::Acquire);
        while !head.is_null() {
            unsafe {
                debug_delay();
                let next =
                    (*head).next.swap(std::ptr::null_mut(), Ordering::Release);
                Box::from_raw(head);
                head = next;
            }
        }
    }
}

struct CacheAccessIter<'a> {
    guard: &'a Guard,
    current_offset: usize,
    current_block: *mut AccessBlock,
}

impl<'a> Iterator for CacheAccessIter<'a> {
    type Item = CacheAccess;

    fn next(&mut self) -> Option<CacheAccess> {
        while !self.current_block.is_null() {
            let current_block = unsafe { &*self.current_block };

            debug_delay();
            if self.current_offset >= MAX_QUEUE_ITEMS {
                let to_drop_ptr = self.current_block;
                debug_delay();
                self.current_block = current_block.next.load(Ordering::Acquire);
                self.current_offset = 0;
                debug_delay();
                let to_drop = unsafe { Box::from_raw(to_drop_ptr) };
                self.guard.defer(|| to_drop);
                continue;
            }

            let mut next = 0;
            while next == 0 {
                // we spin here because there's a race between bumping
                // the offset and setting the value to something other
                // than 0 (and 0 is an invalid value)
                debug_delay();
                next = current_block.block[self.current_offset]
                    .load(Ordering::Acquire);
            }
            self.current_offset += 1;
            return Some(CacheAccess::from(next));
        }

        None
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct CacheAccess {
    // safe because MAX_PID_BITS / N_SHARDS < u32::MAX
    //                     2**37 /   2**8   < 2**32
    pub pid: u32,
    pub sz: u8,
}

impl From<CacheAccess> for u64 {
    fn from(ca: CacheAccess) -> u64 {
        (u64::from(ca.pid) << 8) | u64::from(ca.sz)
    }
}

#[allow(clippy::fallible_impl_from)]
impl From<u64> for CacheAccess {
    fn from(u: u64) -> CacheAccess {
        let sz = usize::try_from((u << 56) >> 56).unwrap();
        assert_ne!(sz, 0);
        let pid = u >> 8;
        assert!(pid < u64::from(u32::MAX));
        CacheAccess {
            pid: u32::try_from(pid).unwrap(),
            sz: u8::try_from(sz).unwrap(),
        }
    }
}

impl CacheAccess {
    fn size(&self) -> usize {
        1 << usize::from(self.sz)
    }

    fn new(pid: PageId, sz: usize) -> CacheAccess {
        let rounded_up_power_of_2 =
            u8::try_from(sz.next_power_of_two().trailing_zeros()).unwrap();

        CacheAccess {
            pid: u32::try_from(pid).expect("expected caller to shift pid down"),
            sz: rounded_up_power_of_2,
        }
    }
}

/// A simple LRU cache.
pub struct Lru {
    shards: Vec<(AccessQueue, FastLock<Shard>)>,
}

impl Lru {
    /// Instantiates a new `Lru` cache.
    pub(crate) fn new(cache_capacity: usize) -> Self {
        assert!(
            cache_capacity >= N_SHARDS,
            "Please configure the cache \
             capacity to be at least 256 bytes"
        );
        let shard_capacity = cache_capacity / N_SHARDS;

        let mut shards = Vec::with_capacity(N_SHARDS);
        shards.resize_with(N_SHARDS, || {
            (AccessQueue::default(), FastLock::new(Shard::new(shard_capacity)))
        });

        Self { shards }
    }

    /// Called when an item is accessed. Returns a Vec of items to be
    /// evicted. Uses flat-combining to avoid blocking on what can
    /// be an asynchronous operation.
    ///
    /// layout:
    ///   items:   1 2 3 4 5 6 7 8 9 10
    ///   shards:  1 0 1 0 1 0 1 0 1 0
    ///   shard 0:   2   4   6   8   10
    ///   shard 1: 1   3   5   7   9
    pub(crate) fn accessed(
        &self,
        id: PageId,
        item_size: usize,
        guard: &Guard,
    ) -> Vec<PageId> {
        const SHARD_BITS: usize = N_SHARDS.trailing_zeros() as usize;

        let mut ret = vec![];
        let shards = N_SHARDS as u64;
        let (shard_idx, shifted_pid) = (id % shards, id >> SHARD_BITS);
        let (access_queue, shard_mu) = &self.shards[safe_usize(shard_idx)];

        let cache_access = CacheAccess::new(shifted_pid, item_size);
        let filled = access_queue.push(cache_access);

        if filled {
            // only try to acquire this if the access queue has filled
            // an entire segment
            if let Some(mut shard) = shard_mu.try_lock() {
                let accesses = access_queue.take(guard);
                for item in accesses {
                    let to_evict = shard.accessed(item);
                    // map shard internal offsets to global items ids
                    for pos in to_evict {
                        let item =
                            (PageId::from(pos) << SHARD_BITS) + shard_idx;
                        ret.push(item);
                    }
                }
            }
        }
        ret
    }
}

#[derive(Eq)]
struct Entry(*mut Node);

unsafe impl Send for Entry {}

impl Ord for Entry {
    fn cmp(&self, other: &Entry) -> std::cmp::Ordering {
        let left_pid: u32 = *self.borrow();
        let right_pid: u32 = *other.borrow();
        left_pid.cmp(&right_pid)
    }
}

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

impl PartialEq for Entry {
    fn eq(&self, other: &Entry) -> bool {
        unsafe { (*self.0).pid == (*other.0).pid }
    }
}

impl BorrowMut<CacheAccess> for Entry {
    fn borrow_mut(&mut self) -> &mut CacheAccess {
        unsafe { &mut *self.0 }
    }
}

impl Borrow<CacheAccess> for Entry {
    fn borrow(&self) -> &CacheAccess {
        unsafe { &*self.0 }
    }
}

impl Borrow<u32> for Entry {
    fn borrow(&self) -> &u32 {
        unsafe { &(*self.0).pid }
    }
}

// we only hash on pid, since we will change
// sz sometimes and we access the item by pid
impl Hash for Entry {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        unsafe { (*self.0).pid.hash(hasher) }
    }
}

struct Shard {
    dll: DoublyLinkedList,
    entries: FastSet8<Entry>,
    capacity: usize,
    size: usize,
}

impl Shard {
    fn new(capacity: usize) -> Self {
        assert!(capacity > 0, "shard capacity must be non-zero");

        Self {
            dll: DoublyLinkedList::default(),
            entries: FastSet8::default(),
            capacity,
            size: 0,
        }
    }

    /// `PageId`s in the shard list are indexes of the entries.
    fn accessed(&mut self, cache_access: CacheAccess) -> Vec<u32> {
        if let Some(entry) = self.entries.get(&cache_access.pid) {
            let old_sz_po2 = unsafe { (*entry.0).swap_sz(cache_access.sz) };
            let old_size = 1 << usize::from(old_sz_po2);

            self.size -= old_size;
            self.dll.promote(entry.0);
        } else {
            let ptr = self.dll.push_head(cache_access);
            self.entries.insert(Entry(ptr));
        };

        self.size += cache_access.size();

        let mut to_evict = vec![];

        while self.size > self.capacity {
            if self.dll.len() == 1 {
                // don't evict what we just added
                break;
            }

            let node = self.dll.pop_tail().unwrap();

            assert!(self.entries.remove(&node.pid));

            to_evict.push(node.pid);

            self.size -= node.size();

            // NB: node is stored in our entries map
            // via a raw pointer, which points to
            // the same allocation used in the DLL.
            // We have to be careful to free node
            // only after removing it from both
            // the DLL and our entries map.
            drop(node);
        }

        to_evict
    }
}

#[inline]
fn safe_usize(value: PageId) -> usize {
    usize::try_from(value).unwrap()
}

#[test]
fn lru_smoke_test() {
    use crate::pin;

    let lru = Lru::new(2);
    for i in 0..1000 {
        let guard = pin();
        lru.accessed(i, 16, &guard);
    }
}

#[test]
fn lru_access_test() {
    use crate::pin;

    let ci = CacheAccess::new(6, 20667);
    assert_eq!(ci.size(), 32 * 1024);

    let lru = Lru::new(4096);

    let guard = pin();

    assert_eq!(lru.accessed(0, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(2, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(4, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(6, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(8, 20667, &guard), vec![0, 2, 4]);
    assert_eq!(lru.accessed(10, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(12, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(14, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(16, 20667, &guard), vec![6, 8, 10, 12]);
    assert_eq!(lru.accessed(18, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(20, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(22, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(24, 20667, &guard), vec![14, 16, 18, 20]);
}
<|endoftext|>