<commit_before>use std::mem;

/// Binding to C++ `std::vector<T, std::allocator<T>>`.
///
/// # Invariants
///
/// As an invariant of this API and the static analysis of the cxx::bridge
/// macro, in Rust code we can never obtain a `CxxVector` by value. Instead in
/// Rust code we will only ever look at a vector behind a reference or smart
/// pointer, as in `&CxxVector<T>` or `UniquePtr<CxxVector<T>>`.
#[repr(C, packed)]
pub struct CxxVector<T> {
    _private: [T; 0],
}

impl<T: VectorElement> CxxVector<T> {
    /// Returns the number of elements in the vector.
    ///
    /// Matches the behavior of C++ [std::vector\<T\>::size][size].
    ///
    /// [size]: https://en.cppreference.com/w/cpp/container/vector/size
    pub fn len(&self) -> usize {
        T::__vector_size(self)
    }

    /// Returns true if the vector contains no elements.
    ///
    /// Matches the behavior of C++ [std::vector\<T\>::empty][empty].
    ///
    /// [empty]: https://en.cppreference.com/w/cpp/container/vector/empty
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns a reference to an element at the given position, or `None` if
    /// out of bounds.
    pub fn get(&self, pos: usize) -> Option<&T> {
        if pos < self.len() {
            Some(unsafe { T::__get_unchecked(self, pos) })
        } else {
            None
        }
    }

    /// Returns a reference to an element without doing bounds checking.
    ///
    /// This is generally not recommended, use with caution! Calling this method
    /// with an out-of-bounds index is undefined behavior even if the resulting
    /// reference is not used.
    ///
    /// Matches the behavior of C++
    /// [std::vector\<T\>::operator\[\]][operator_at].
    ///
    /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at
    pub unsafe fn get_unchecked(&self, pos: usize) -> &T {
        T::__get_unchecked(self, pos)
    }

    /// Appends an element to the back of the vector.
    pub fn push_back(&mut self, item: &T) {
        T::__push_back(self, item);
    }
}

pub struct Iter<'a, T> {
    v: &'a CxxVector<T>,
    index: usize,
}

impl<'a, T: VectorElement> IntoIterator for &'a CxxVector<T> {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        Iter { v: self, index: 0 }
    }
}

impl<'a, T: VectorElement> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        self.index = self.index + 1;
        self.v.get(self.index - 1)
    }
}

// Methods are private; not intended to be implemented outside of cxxbridge
// codebase.
#[doc(hidden)]
pub unsafe trait VectorElement: Sized {
    fn __vector_size(v: &CxxVector<Self>) -> usize;
    unsafe fn __get_unchecked(v: &CxxVector<Self>, pos: usize) -> &Self;
    fn __push_back(v: &CxxVector<Self>, item: &Self);
}

macro_rules! impl_vector_element_for_primitive {
    ($ty:ident) => {
        unsafe impl VectorElement for $ty {
            fn __vector_size(v: &CxxVector<$ty>) -> usize {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$size")]
                        fn __vector_size(_: &CxxVector<$ty>) -> usize;
                    }
                }
                unsafe { __vector_size(v) }
            }
            unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$get_unchecked")]
                        fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty;
                    }
                }
                &*__get_unchecked(v, pos)
            }
            fn __push_back(v: &CxxVector<$ty>, item: &$ty) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$push_back")]
                        fn __push_back(_: &CxxVector<$ty>, _: &$ty);
                    }
                }
                unsafe { __push_back(v, item) }
            }
        }
    };
}

impl_vector_element_for_primitive!(u8);
impl_vector_element_for_primitive!(u16);
impl_vector_element_for_primitive!(u32);
impl_vector_element_for_primitive!(u64);
impl_vector_element_for_primitive!(usize);
impl_vector_element_for_primitive!(i8);
impl_vector_element_for_primitive!(i16);
impl_vector_element_for_primitive!(i32);
impl_vector_element_for_primitive!(i64);
impl_vector_element_for_primitive!(isize);
impl_vector_element_for_primitive!(f32);
impl_vector_element_for_primitive!(f64);

const_assert_eq!(1, mem::align_of::<CxxVector<usize>>());
<commit_msg>Prefer where-clause over trait bounds in CxxVector<commit_after>use std::mem;

/// Binding to C++ `std::vector<T, std::allocator<T>>`.
///
/// # Invariants
///
/// As an invariant of this API and the static analysis of the cxx::bridge
/// macro, in Rust code we can never obtain a `CxxVector` by value. Instead in
/// Rust code we will only ever look at a vector behind a reference or smart
/// pointer, as in `&CxxVector<T>` or `UniquePtr<CxxVector<T>>`.
#[repr(C, packed)]
pub struct CxxVector<T> {
    _private: [T; 0],
}

impl<T> CxxVector<T>
where
    T: VectorElement,
{
    /// Returns the number of elements in the vector.
    ///
    /// Matches the behavior of C++ [std::vector\<T\>::size][size].
    ///
    /// [size]: https://en.cppreference.com/w/cpp/container/vector/size
    pub fn len(&self) -> usize {
        T::__vector_size(self)
    }

    /// Returns true if the vector contains no elements.
    ///
    /// Matches the behavior of C++ [std::vector\<T\>::empty][empty].
    ///
    /// [empty]: https://en.cppreference.com/w/cpp/container/vector/empty
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns a reference to an element at the given position, or `None` if
    /// out of bounds.
    pub fn get(&self, pos: usize) -> Option<&T> {
        if pos < self.len() {
            Some(unsafe { T::__get_unchecked(self, pos) })
        } else {
            None
        }
    }

    /// Returns a reference to an element without doing bounds checking.
    ///
    /// This is generally not recommended, use with caution! Calling this method
    /// with an out-of-bounds index is undefined behavior even if the resulting
    /// reference is not used.
    ///
    /// Matches the behavior of C++
    /// [std::vector\<T\>::operator\[\]][operator_at].
    ///
    /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at
    pub unsafe fn get_unchecked(&self, pos: usize) -> &T {
        T::__get_unchecked(self, pos)
    }

    /// Appends an element to the back of the vector.
    pub fn push_back(&mut self, item: &T) {
        T::__push_back(self, item);
    }
}

pub struct Iter<'a, T> {
    v: &'a CxxVector<T>,
    index: usize,
}

impl<'a, T> IntoIterator for &'a CxxVector<T>
where
    T: VectorElement,
{
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        Iter { v: self, index: 0 }
    }
}

impl<'a, T> Iterator for Iter<'a, T>
where
    T: VectorElement,
{
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        self.index = self.index + 1;
        self.v.get(self.index - 1)
    }
}

// Methods are private; not intended to be implemented outside of cxxbridge
// codebase.
#[doc(hidden)]
pub unsafe trait VectorElement: Sized {
    fn __vector_size(v: &CxxVector<Self>) -> usize;
    unsafe fn __get_unchecked(v: &CxxVector<Self>, pos: usize) -> &Self;
    fn __push_back(v: &CxxVector<Self>, item: &Self);
}

macro_rules! impl_vector_element_for_primitive {
    ($ty:ident) => {
        unsafe impl VectorElement for $ty {
            fn __vector_size(v: &CxxVector<$ty>) -> usize {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$size")]
                        fn __vector_size(_: &CxxVector<$ty>) -> usize;
                    }
                }
                unsafe { __vector_size(v) }
            }
            unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$get_unchecked")]
                        fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty;
                    }
                }
                &*__get_unchecked(v, pos)
            }
            fn __push_back(v: &CxxVector<$ty>, item: &$ty) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge02$std$vector$", stringify!($ty), "$push_back")]
                        fn __push_back(_: &CxxVector<$ty>, _: &$ty);
                    }
                }
                unsafe { __push_back(v, item) }
            }
        }
    };
}

impl_vector_element_for_primitive!(u8);
impl_vector_element_for_primitive!(u16);
impl_vector_element_for_primitive!(u32);
impl_vector_element_for_primitive!(u64);
impl_vector_element_for_primitive!(usize);
impl_vector_element_for_primitive!(i8);
impl_vector_element_for_primitive!(i16);
impl_vector_element_for_primitive!(i32);
impl_vector_element_for_primitive!(i64);
impl_vector_element_for_primitive!(isize);
impl_vector_element_for_primitive!(f32);
impl_vector_element_for_primitive!(f64);

const_assert_eq!(1, mem::align_of::<CxxVector<usize>>());
<|endoftext|>


<commit_before>use super::book::{Book, BookAccounting};
use std::collections::HashMap;
use std::fmt;
use std::cmp::Ordering;
use ::error::PoloError;
use super::timeseries::Timeseries;
use super::tradestats::{TradeStats, TimeStats};
use time;

#[derive(Clone, Debug, PartialEq)]
pub struct Record {
  pub rate: f64,
  pub amount: f64
}

#[derive(Clone, Debug, PartialEq)]
pub struct BookStats {
  pub min_sell: f64,
  pub max_buy: f64,
  pub sum_sell: f64,
  pub sum_buy: f64,
  pub vec_buy: Vec<Record>,
  pub vec_sell: Vec<Record>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct BookWithStats {
  book: Book,
  pub stats: BookStats,
  pub trade_series_1s: Timeseries<TradeStats>,
  trade_stats_1m: TradeStats,
}

// BookStats operations

impl BookStats {
  pub fn new(book: &Book) -> BookStats {
    let (sum_buy, mut vec_buy) = hash_to_vec(&book.buy);
    vec_buy.sort_unstable_by(|rec1, rec2| f64cmp(&rec2.rate, &rec1.rate));
    let (sum_sell, mut vec_sell) = hash_to_vec(&book.sell);
    vec_sell.sort_unstable_by(|rec1, rec2| f64cmp(&rec1.rate, &rec2.rate));

    BookStats {
      min_sell: vec_sell.first().map_or(0.0, |rec| rec.rate),
      max_buy: vec_buy.first().map_or(0.0, |rec| rec.rate),
      sum_sell,
      sum_buy,
      vec_buy,
      vec_sell,
    }
  }

  pub fn update_sell(&mut self, rate: f64, amount: f64, prev_amount: Option<f64>) {
    let idx_r = self.vec_sell.binary_search_by(|rec| f64cmp(&rec.rate, &rate));
    let stat_cmp = self.min_sell > rate;
    update_sorted_vec(idx_r, &mut self.vec_sell, &mut self.min_sell, rate, amount, stat_cmp);
    self.sum_sell = self.sum_sell + amount - prev_amount.unwrap_or(0.0);
  }

  pub fn update_buy(&mut self, rate: f64, amount: f64, prev_amount: Option<f64>) {
    let idx_r = self.vec_buy.binary_search_by(|rec| f64cmp(&rate, &rec.rate));
    let stat_cmp = self.max_buy < rate;
    update_sorted_vec(idx_r, &mut self.vec_buy, &mut self.max_buy, rate, amount, stat_cmp);
    self.sum_buy = self.sum_buy + amount - prev_amount.unwrap_or(0.0);
  }
}

impl fmt::Display for BookStats {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "(BUY min {} sum {} | SELL max {} sum {})", self.max_buy, self.sum_buy, self.min_sell, self.sum_sell)
  }
}

// BookWithStats operations

impl BookWithStats {
  pub fn new(book: Book) -> BookWithStats {
    BookWithStats {
      stats: BookStats::new(&book),
      trade_series_1s: Timeseries::new(),
      trade_stats_1m: TradeStats::default(),
      book
    }
  }
}

impl fmt::Display for BookWithStats {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    self.stats.fmt(f)?;
    if let Some(last_sec) = self.trade_series_1s.data.front() {
      last_sec.fmt(f)?;
    }
    self.trade_stats_1m.fmt(f)
  }
}

impl BookAccounting for BookWithStats {
  fn update_sell(&mut self, rate: String, amount: f64) -> Option<f64> {
    let rate_f64 = rate.parse::<f64>();
    let prev_amount = self.book.update_sell(rate, amount);
    if let Ok(rate_f64) = rate_f64 {
      self.stats.update_sell(rate_f64, amount, prev_amount);
    };
    prev_amount
  }

  fn update_buy(&mut self, rate: String, amount: f64) -> Option<f64> {
    let rate_f64 = rate.parse::<f64>();
    let prev_amount = self.book.update_buy(rate, amount);
    if let Ok(rate_f64) = rate_f64 {
      self.stats.update_buy(rate_f64, amount, prev_amount);
    };
    prev_amount
  }

  fn new_deal(&mut self, id: u64, rate: String, amount: f64) -> Result<f64, PoloError> {
    let rate_f64 = self.book.new_deal(id, rate, amount)?;
    Ok(rate_f64)
  }

  fn book_ref(&self) -> &Book {
    &self.book
  }
}

impl TimeStats for BookWithStats {
  fn update_stats_1s(&mut self) {
    let timestamp = time::get_time();
    // cleanup deals time series
    self.book.deals.drain_until(time::Timespec { sec: timestamp.sec - 600, nsec: 0 });
    // update stats with last second of data
    let last_second = self.book.deals.vec_after(time::Timespec { sec: timestamp.sec - 1, nsec: timestamp.nsec });
    let stats = TradeStats::new(last_second);
    self.trade_stats_1m = self.trade_stats_1m + &stats;
    self.trade_series_1s.add(stats);
    if self.trade_series_1s.data.len() > 60 {
      let stats_1m_ago = &self.trade_series_1s.data[self.trade_series_1s.data.len() - 60];
      self.trade_stats_1m = self.trade_stats_1m - stats_1m_ago;
    }
  }
}

/**
 ** Library functions
 **/

fn f64cmp(f1: &f64, f2: &f64) -> Ordering {
  f1.partial_cmp(f2).unwrap_or(Ordering::Equal)
}

fn hash_to_vec(hash: &HashMap<String, f64>) -> (f64, Vec<Record>) {
  let mut sum = 0.0;
  let vec = hash.iter().filter_map(|(rate_s, amount)| {
    rate_s.parse::<f64>()
      .and_then(|rate| {
        sum += *amount;
        Ok(Record { rate, amount: *amount })
      }).ok()
  }).collect();
  (sum, vec)
}

fn update_sorted_vec(idx_r: Result<usize, usize>, vec: &mut Vec<Record>, stat: &mut f64, rate: f64, amount: f64, stat_cmp: bool) {
  if amount == 0.0 {
    if let Ok(idx) = idx_r {
      vec.remove(idx);
    };
    if *stat == rate || *stat == 0.0 {
      *stat = vec.first().map_or(*stat, |rec| rec.rate);
    }
  } else if amount > 0.0  {
    match idx_r {
      Ok(idx) => vec[idx].amount = amount,
      Err(idx) => vec.insert(idx, Record { rate, amount })
    };
    if stat_cmp || *stat == 0.0 {
      *stat = rate
    }
  };
}

/**
 ** TESTS TESTS TESTS
 **/

#[cfg(test)]
mod tests {
  use super::BookWithStats;
  use ::data::book::Book;
  use json;
  use std::convert::TryFrom;

  #[test]
  fn stats_init() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let book_stats = BookWithStats::new(book).stats;
    assert_eq!(book_stats.min_sell, 0.13161901);
    assert_eq!(book_stats.max_buy, 0.13109621);
  }

  #[test]
  fn stats_init_wrong_order() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13361901": 0.23709568, "0.13164313": "0.17328089"}, {"0.12909621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let book_stats = BookWithStats::new(book).stats;
    assert_eq!(book_stats.min_sell, 0.13164313);
    assert_eq!(book_stats.max_buy, 0.13069621);
  }

  #[test]
  fn stats_update_sell_zero() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_sell(0.1, 0.0, None);
    assert_eq!(book_stats.min_sell, 0.13161901);
  }

  #[test]
  fn stats_update_sell_shift_zero() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_sell(0.13161901, 0.0, Some(0.23709568));
    assert_eq!(book_stats.min_sell, 0.13164313);
  }

  #[test]
  fn stats_update_sell() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_sell(0.1, 1.0, None);
    assert_eq!(book_stats.min_sell, 0.1);
  }

  #[test]
  fn stats_update_buy_zero() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_buy(100.0, 0.0, None);
    assert_eq!(book_stats.max_buy, 0.13109621);
  }

  #[test]
  fn stats_update_buy_shift_zero() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_buy(0.13109621, 0.0, Some(0.2331));
    assert_eq!(book_stats.max_buy, 0.13069621);
  }

  #[test]
  fn stats_update_buy() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_buy(100.0, 1.0, None);
    assert_eq!(book_stats.max_buy, 100.0);
  }

}<commit_msg>some debug info<commit_after>use super::book::{Book, BookAccounting};
use std::collections::HashMap;
use std::fmt;
use std::cmp::Ordering;
use ::error::PoloError;
use super::timeseries::Timeseries;
use super::tradestats::{TradeStats, TimeStats};
use time;

#[derive(Clone, Debug, PartialEq)]
pub struct Record {
  pub rate: f64,
  pub amount: f64
}

#[derive(Clone, Debug, PartialEq)]
pub struct BookStats {
  pub min_sell: f64,
  pub max_buy: f64,
  pub sum_sell: f64,
  pub sum_buy: f64,
  pub vec_buy: Vec<Record>,
  pub vec_sell: Vec<Record>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct BookWithStats {
  book: Book,
  pub stats: BookStats,
  pub trade_series_1s: Timeseries<TradeStats>,
  trade_stats_1m: TradeStats,
}

// BookStats operations

impl BookStats {
  pub fn new(book: &Book) -> BookStats {
    let (sum_buy, mut vec_buy) = hash_to_vec(&book.buy);
    vec_buy.sort_unstable_by(|rec1, rec2| f64cmp(&rec2.rate, &rec1.rate));
    let (sum_sell, mut vec_sell) = hash_to_vec(&book.sell);
    vec_sell.sort_unstable_by(|rec1, rec2| f64cmp(&rec1.rate, &rec2.rate));

    BookStats {
      min_sell: vec_sell.first().map_or(0.0, |rec| rec.rate),
      max_buy: vec_buy.first().map_or(0.0, |rec| rec.rate),
      sum_sell,
      sum_buy,
      vec_buy,
      vec_sell,
    }
  }

  pub fn update_sell(&mut self, rate: f64, amount: f64, prev_amount: Option<f64>) {
    let idx_r = self.vec_sell.binary_search_by(|rec| f64cmp(&rec.rate, &rate));
    let stat_cmp = self.min_sell > rate;
    update_sorted_vec(idx_r, &mut self.vec_sell, &mut self.min_sell, rate, amount, stat_cmp);
    self.sum_sell = self.sum_sell + amount - prev_amount.unwrap_or(0.0);
  }

  pub fn update_buy(&mut self, rate: f64, amount: f64, prev_amount: Option<f64>) {
    let idx_r = self.vec_buy.binary_search_by(|rec| f64cmp(&rate, &rec.rate));
    let stat_cmp = self.max_buy < rate;
    update_sorted_vec(idx_r, &mut self.vec_buy, &mut self.max_buy, rate, amount, stat_cmp);
    self.sum_buy = self.sum_buy + amount - prev_amount.unwrap_or(0.0);
  }
}

impl fmt::Display for BookStats {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "(BUY min {} sum {} | SELL max {} sum {})", self.max_buy, self.sum_buy, self.min_sell, self.sum_sell)
  }
}

// BookWithStats operations

impl BookWithStats {
  pub fn new(book: Book) -> BookWithStats {
    BookWithStats {
      stats: BookStats::new(&book),
      trade_series_1s: Timeseries::new(),
      trade_stats_1m: TradeStats::default(),
      book
    }
  }
}

impl fmt::Display for BookWithStats {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    self.stats.fmt(f)?;
    write!(f, "\nstats ");
    if let Some(last_sec) = self.trade_series_1s.data.front() {
      write!(f, "1s: ");
      last_sec.fmt(f)?;
      write!(f, " | ");
    }
    write!(f, "1m: ");
    self.trade_stats_1m.fmt(f)
  }
}

impl BookAccounting for BookWithStats {
  fn update_sell(&mut self, rate: String, amount: f64) -> Option<f64> {
    let rate_f64 = rate.parse::<f64>();
    let prev_amount = self.book.update_sell(rate, amount);
    if let Ok(rate_f64) = rate_f64 {
      self.stats.update_sell(rate_f64, amount, prev_amount);
    };
    prev_amount
  }

  fn update_buy(&mut self, rate: String, amount: f64) -> Option<f64> {
    let rate_f64 = rate.parse::<f64>();
    let prev_amount = self.book.update_buy(rate, amount);
    if let Ok(rate_f64) = rate_f64 {
      self.stats.update_buy(rate_f64, amount, prev_amount);
    };
    prev_amount
  }

  fn new_deal(&mut self, id: u64, rate: String, amount: f64) -> Result<f64, PoloError> {
    let rate_f64 = self.book.new_deal(id, rate, amount)?;
    Ok(rate_f64)
  }

  fn book_ref(&self) -> &Book {
    &self.book
  }
}

impl TimeStats for BookWithStats {
  fn update_stats_1s(&mut self) {
    let timestamp = time::get_time();
    // cleanup deals time series
    self.book.deals.drain_until(time::Timespec { sec: timestamp.sec - 600, nsec: 0 });
    // update stats with last second of data
    let last_second = self.book.deals.vec_after(time::Timespec { sec: timestamp.sec - 1, nsec: timestamp.nsec });
    let stats = TradeStats::new(last_second);
    println!("{:?}", stats);
    self.trade_stats_1m = self.trade_stats_1m + &stats;
    println!("{:?}", self.trade_stats_1m);
    self.trade_series_1s.add(stats);
    if self.trade_series_1s.data.len() > 60 {
      let stats_1m_ago = &self.trade_series_1s.data[self.trade_series_1s.data.len() - 60];
      self.trade_stats_1m = self.trade_stats_1m - stats_1m_ago;
    }
  }
}

/**
 ** Library functions
 **/

fn f64cmp(f1: &f64, f2: &f64) -> Ordering {
  f1.partial_cmp(f2).unwrap_or(Ordering::Equal)
}

fn hash_to_vec(hash: &HashMap<String, f64>) -> (f64, Vec<Record>) {
  let mut sum = 0.0;
  let vec = hash.iter().filter_map(|(rate_s, amount)| {
    rate_s.parse::<f64>()
      .and_then(|rate| {
        sum += *amount;
        Ok(Record { rate, amount: *amount })
      }).ok()
  }).collect();
  (sum, vec)
}

fn update_sorted_vec(idx_r: Result<usize, usize>, vec: &mut Vec<Record>, stat: &mut f64, rate: f64, amount: f64, stat_cmp: bool) {
  if amount == 0.0 {
    if let Ok(idx) = idx_r {
      vec.remove(idx);
    };
    if *stat == rate || *stat == 0.0 {
      *stat = vec.first().map_or(*stat, |rec| rec.rate);
    }
  } else if amount > 0.0  {
    match idx_r {
      Ok(idx) => vec[idx].amount = amount,
      Err(idx) => vec.insert(idx, Record { rate, amount })
    };
    if stat_cmp || *stat == 0.0 {
      *stat = rate
    }
  };
}

/**
 ** TESTS TESTS TESTS
 **/

#[cfg(test)]
mod tests {
  use super::BookWithStats;
  use ::data::book::Book;
  use json;
  use std::convert::TryFrom;

  #[test]
  fn stats_init() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let book_stats = BookWithStats::new(book).stats;
    assert_eq!(book_stats.min_sell, 0.13161901);
    assert_eq!(book_stats.max_buy, 0.13109621);
  }

  #[test]
  fn stats_init_wrong_order() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13361901": 0.23709568, "0.13164313": "0.17328089"}, {"0.12909621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let book_stats = BookWithStats::new(book).stats;
    assert_eq!(book_stats.min_sell, 0.13164313);
    assert_eq!(book_stats.max_buy, 0.13069621);
  }

  #[test]
  fn stats_update_sell_zero() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_sell(0.1, 0.0, None);
    assert_eq!(book_stats.min_sell, 0.13161901);
  }

  #[test]
  fn stats_update_sell_shift_zero() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_sell(0.13161901, 0.0, Some(0.23709568));
    assert_eq!(book_stats.min_sell, 0.13164313);
  }

  #[test]
  fn stats_update_sell() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_sell(0.1, 1.0, None);
    assert_eq!(book_stats.min_sell, 0.1);
  }

  #[test]
  fn stats_update_buy_zero() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_buy(100.0, 0.0, None);
    assert_eq!(book_stats.max_buy, 0.13109621);
  }

  #[test]
  fn stats_update_buy_shift_zero() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_buy(0.13109621, 0.0, Some(0.2331));
    assert_eq!(book_stats.max_buy, 0.13069621);
  }

  #[test]
  fn stats_update_buy() {
    let book_init = r#"{"currencyPair": "BTC_BCH", "orderBook": [{"0.13161901": 0.23709568, "0.13164313": "0.17328089"}, {"0.13109621": 0.2331, "0.13069621": 0.2331}]}"#;
    let book = Book::try_from(&json::parse(book_init).unwrap()).unwrap();
    let mut book_stats = BookWithStats::new(book).stats;
    book_stats.update_buy(100.0, 1.0, None);
    assert_eq!(book_stats.max_buy, 100.0);
  }

}<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;
use std::fmt;
use std::str;
use std::cmp;
use super::gl;

use Capabilities;
use shade;

/// A version number for a specific component of an OpenGL implementation
#[deriving(Eq, PartialEq)]
pub struct Version {
    pub major: uint,
    pub minor: uint,
    pub revision: Option<uint>,
    pub vendor_info: &'static str,
}

// FIXME https://github.com/rust-lang/rust/issues/18738
// derive PartialOrd, Ord

impl cmp::PartialOrd for Version {
    fn partial_cmp(&self, other: &Version) -> Option<cmp::Ordering> {
        if self.eq(other) {
            Some(cmp::Equal)
        } else {
            let lt =
                self.major < other.major
             || self.minor < other.minor
             || self.revision < other.revision
             || self.vendor_info < other.vendor_info;
            if lt {
                Some(cmp::Less)
            } else {
                Some(cmp::Greater)
            }
        }
    }
}

impl cmp::Ord for Version {
    fn cmp(&self, other: &Version) -> cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: uint, minor: uint, revision: Option<uint>,
               vendor_info: &'static str) -> Version {
        Version {
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(src: &'static str) -> Result<Version, &'static str> {
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (src.slice_to(i), src.slice_from(i + 1)),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(from_str);
        let minor = it.next().and_then(from_str);
        let revision = it.next().and_then(from_str);

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Show for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = unsafe { gl.GetString(name) } as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { str::raw::c_str_to_static_slice(ptr) }
    } else {
        panic!("Invalid GLenum passed to `get_string`: {:x}", name)
    }
}

fn get_uint(gl: &gl::Gl, name: gl::types::GLenum) -> uint {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as uint
}

/// A unique platform identifier that does not change between releases
#[deriving(Eq, PartialEq, Show)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// OpenGL implementation information
#[deriving(Show)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 2, None, "") {
            let num_exts = get_uint(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            range(0, num_exts).map(|i| {
                unsafe {
                    str::raw::c_str_to_static_slice(
                        gl.GetStringi(gl::EXTENSIONS, i) as *const i8,
                    )
                }
            }).collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &str) -> bool {
        self.extensions.contains_equiv(s)
    }
}

fn to_shader_model(v: &Version) -> shade::ShaderModel {
    match *v {
        v if v < Version::new(1, 20, None, "") => shade::ModelUnsupported,
        v if v < Version::new(1, 50, None, "") => shade::Model30,
        v if v < Version::new(3,  0, None, "") => shade::Model40,
        v if v < Version::new(4, 30, None, "") => shade::Model41,
        _                                      => shade::Model50,
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities) {
    let info = Info::get(gl);
    let caps = Capabilities {
        shader_model: to_shader_model(&info.shading_language),
        max_draw_buffers: get_uint(gl, gl::MAX_DRAW_BUFFERS),
        max_texture_size: get_uint(gl, gl::MAX_TEXTURE_SIZE),
        max_vertex_attributes: get_uint(gl, gl::MAX_VERTEX_ATTRIBS),
        uniform_block_supported: info.version >= Version::new(3, 1, None, "")
            || info.is_extension_supported("GL_ARB_uniform_buffer_object"),
        array_buffer_supported: info.version >= Version::new(3, 0, None, "")
            || info.is_extension_supported("GL_ARB_vertex_array_object"),
        immutable_storage_supported: info.version >= Version::new(4, 2, None, "")
            || info.is_extension_supported("GL_ARB_texture_storage"),
        sampler_objects_supported: info.version >= Version::new(3, 3, None, "")
            || info.is_extension_supported("GL_ARB_sampler_objects"),
        instance_call_supported: info.version >= Version::new(3, 1, None, "")
            || info.is_extension_supported("GL_ARB_draw_instanced"),
        instance_rate_supported: info.version >= Version::new(3, 3, None, "")
            || info.is_extension_supported("GL_ARB_instanced_arrays"),
        render_targets_supported: info.version >= Version::new(3, 0, None, "")
            || info.is_extension_supported("GL_ARB_framebuffer_object"),
        vertex_base_supported: info.version >= Version::new(3, 2, None, "")
            || info.is_extension_supported("GL_ARB_draw_elements_base_vertex"),
        instance_base_supported: info.version >= Version::new(4, 2, None, "")
            || info.is_extension_supported("GL_ARB_base_instance"),
    };
    (info, caps)
}

#[cfg(test)]
mod tests {
    use super::Version;
    use super::to_shader_model;
    use shade;

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version::new(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version::new(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version::new(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld")));
    }

    #[test]
    fn test_shader_model() {
        assert_eq!(to_shader_model(&Version::parse("1.10").unwrap()), shade::ModelUnsupported);
        assert_eq!(to_shader_model(&Version::parse("1.20").unwrap()), shade::Model30);
        assert_eq!(to_shader_model(&Version::parse("1.50").unwrap()), shade::Model40);
        assert_eq!(to_shader_model(&Version::parse("3.00").unwrap()), shade::Model41);
        assert_eq!(to_shader_model(&Version::parse("4.30").unwrap()), shade::Model50);
    }
}
<commit_msg>Clean up version/extension querying a bit<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;
use std::fmt;
use std::str;
use std::cmp;
use super::gl;

use Capabilities;
use shade;

/// A version number for a specific component of an OpenGL implementation
#[deriving(Eq, PartialEq)]
pub struct Version {
    pub major: u32,
    pub minor: u32,
    pub revision: Option<u32>,
    pub vendor_info: &'static str,
}

// FIXME https://github.com/rust-lang/rust/issues/18738
// derive PartialOrd, Ord

impl cmp::PartialOrd for Version {
    fn partial_cmp(&self, other: &Version) -> Option<cmp::Ordering> {
        if self.eq(other) {
            Some(cmp::Equal)
        } else {
            let lt =
                self.major < other.major
             || self.minor < other.minor
             || self.revision < other.revision
             || self.vendor_info < other.vendor_info;
            if lt {
                Some(cmp::Less)
            } else {
                Some(cmp::Greater)
            }
        }
    }
}

impl cmp::Ord for Version {
    fn cmp(&self, other: &Version) -> cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: u32, minor: u32, revision: Option<u32>,
               vendor_info: &'static str) -> Version {
        Version {
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(src: &'static str) -> Result<Version, &'static str> {
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (src.slice_to(i), src.slice_from(i + 1)),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(from_str);
        let minor = it.next().and_then(from_str);
        let revision = it.next().and_then(from_str);

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Show for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = unsafe { gl.GetString(name) } as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { str::raw::c_str_to_static_slice(ptr) }
    } else {
        panic!("Invalid GLenum passed to `get_string`: {:x}", name)
    }
}

fn get_uint(gl: &gl::Gl, name: gl::types::GLenum) -> uint {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as uint
}

/// A unique platform identifier that does not change between releases
#[deriving(Eq, PartialEq, Show)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// OpenGL implementation information
#[deriving(Show)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 2, None, "") {
            let num_exts = get_uint(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            range(0, num_exts).map(|i| {
                unsafe {
                    str::raw::c_str_to_static_slice(
                        gl.GetStringi(gl::EXTENSIONS, i) as *const i8,
                    )
                }
            }).collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &str) -> bool {
        self.extensions.contains_equiv(s)
    }

    pub fn is_version_or_extension_supported(&self, major: u32, minor: u32, ext: &str) -> bool {
        self.version >= Version::new(major, minor, None, "") || self.is_extension_supported(ext)
    }
}

fn to_shader_model(v: &Version) -> shade::ShaderModel {
    match *v {
        v if v < Version::new(1, 20, None, "") => shade::ModelUnsupported,
        v if v < Version::new(1, 50, None, "") => shade::Model30,
        v if v < Version::new(3,  0, None, "") => shade::Model40,
        v if v < Version::new(4, 30, None, "") => shade::Model41,
        _                                      => shade::Model50,
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities) {
    let info = Info::get(gl);
    let caps = Capabilities {
        shader_model: to_shader_model(&info.shading_language),
        max_draw_buffers: get_uint(gl, gl::MAX_DRAW_BUFFERS),
        max_texture_size: get_uint(gl, gl::MAX_TEXTURE_SIZE),
        max_vertex_attributes: get_uint(gl, gl::MAX_VERTEX_ATTRIBS),
        uniform_block_supported:
            info.is_version_or_extension_supported(3, 0, "GL_ARB_uniform_buffer_object"),
        array_buffer_supported:
            info.is_version_or_extension_supported(3, 0, "GL_ARB_vertex_array_object"),
        immutable_storage_supported:
            info.is_version_or_extension_supported(4, 2, "GL_ARB_texture_storage"),
        sampler_objects_supported:
            info.is_version_or_extension_supported(3, 3, "GL_ARB_sampler_objects"),
        instance_call_supported:
            info.is_version_or_extension_supported(3, 1, "GL_ARB_draw_instanced"),
        instance_rate_supported:
            info.is_version_or_extension_supported(3, 3, "GL_ARB_instanced_arrays"),
        render_targets_supported:
            info.is_version_or_extension_supported(3, 0, "GL_ARB_framebuffer_object"),
        vertex_base_supported:
            info.is_version_or_extension_supported(3, 2, "GL_ARB_draw_elements_base_vertex"),
        instance_base_supported:
            info.is_version_or_extension_supported(4, 2, "GL_ARB_base_instance"),
    };
    (info, caps)
}

#[cfg(test)]
mod tests {
    use super::Version;
    use super::to_shader_model;
    use shade;

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version::new(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version::new(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version::new(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld")));
    }

    #[test]
    fn test_shader_model() {
        assert_eq!(to_shader_model(&Version::parse("1.10").unwrap()), shade::ModelUnsupported);
        assert_eq!(to_shader_model(&Version::parse("1.20").unwrap()), shade::Model30);
        assert_eq!(to_shader_model(&Version::parse("1.50").unwrap()), shade::Model40);
        assert_eq!(to_shader_model(&Version::parse("3.00").unwrap()), shade::Model41);
        assert_eq!(to_shader_model(&Version::parse("4.30").unwrap()), shade::Model50);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fs::File;
use std::io;
use std::io::{Read, Seek, SeekFrom};
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::mpsc::TryRecvError;
use std::thread;
use std::time::Duration;

use mio::event::Evented;
use mio::unix::{EventedFd, UnixReady};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel;

use gpio::sysfs;

pub use gpio::sysfs::Direction;
pub use gpio::{Level, Trigger};

quick_error! {
    #[derive(Debug)]
/// Errors that can occur while working with interrupts.
    pub enum Error {
/// Synchronous interrupt isn't initialized.
        NotInitialized { description("not initialized") }
/// Time out.
        TimeOut { description("interrupt polling timed out while waiting for a trigger") }
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// Sysfs error.
        Sysfs(err: sysfs::Error) { description(err.description()) from() }
/// IO error while communicating with the interrupt polling thread.
        SendIo(err: io::Error) { description(err.description()) }
/// Disconnected while sending a control message to the interrupt polling thread.
        SendDisconnected { description("receiving half of the channel has disconnected") }
/// Interrupt polling thread panicked.
        ThreadPanic { description("interrupt polling thread panicked") }
    }
}

impl<T> From<channel::SendError<T>> for Error {
    fn from(err: channel::SendError<T>) -> Error {
        match err {
            channel::SendError::Io(e) => Error::SendIo(e),
            channel::SendError::Disconnected(_) => Error::SendDisconnected,
        }
    }
}

/// Result type returned from methods that can have `rppal::gpio::interrupt::Error`s.
pub type Result<T> = result::Result<T, Error>;

const TOKEN_RX: usize = 0;
const TOKEN_PIN: usize = 1;

struct Interrupt {
    pin: u8,
    trigger: Trigger,
    sysfs_value: File,
}

impl Interrupt {
    fn new(pin: u8, trigger: Trigger) -> Result<Interrupt> {
        // Export the GPIO pin so we can configure it through sysfs, set its mode to
        // input, and set the trigger type.
        sysfs::export(pin)?;
        sysfs::set_direction(pin, Direction::In)?;
        sysfs::set_edge(pin, trigger)?;

        Ok(Interrupt {
            pin,
            trigger,
            sysfs_value: sysfs::open_value(pin)?,
        })
    }

    fn trigger(&self) -> Trigger {
        self.trigger
    }

    fn set_trigger(&mut self, trigger: Trigger) -> Result<()> {
        self.trigger = trigger;
        sysfs::set_edge(self.pin, trigger)?;

        Ok(())
    }

    fn level(&mut self) -> Result<Level> {
        let mut buffer = [0; 1];
        self.sysfs_value.read_exact(&mut buffer)?;
        self.sysfs_value.seek(SeekFrom::Start(0))?;

        match &buffer {
            b"0" => Ok(Level::Low),
            _ => Ok(Level::High),
        }
    }
}

impl Drop for Interrupt {
    fn drop(&mut self) {
        sysfs::unexport(self.pin).ok();
    }
}

impl Evented for Interrupt {
    fn register(
        &self,
        poll: &Poll,
        token: Token,
        interest: Ready,
        opts: PollOpt,
    ) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).register(poll, token, interest, opts)
    }

    fn reregister(
        &self,
        poll: &Poll,
        token: Token,
        interest: Ready,
        opts: PollOpt,
    ) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &Poll) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).deregister(poll)
    }
}

struct TriggerStatus {
    interrupt: Option<Interrupt>,
    triggered: bool,
    level: Level,
}

pub struct EventLoop {
    poll: Poll,
    events: Events,
    trigger_status: Vec<TriggerStatus>,
}

impl EventLoop {
    pub fn new(capacity: usize) -> Result<EventLoop> {
        let mut trigger_status = Vec::with_capacity(capacity);

        // Initialize trigger_status while circumventing the Copy/Clone requirement
        for _ in 0..trigger_status.capacity() {
            trigger_status.push(TriggerStatus {
                interrupt: None,
                triggered: false,
                level: Level::Low,
            });
        }

        Ok(EventLoop {
            poll: Poll::new()?,
            events: Events::with_capacity(capacity),
            trigger_status,
        })
    }

    pub fn poll(
        &mut self,
        pins: &[u8],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<(u8, Level)> {
        for pin in pins {
            if *pin as usize >= self.trigger_status.capacity() {
                return Err(Error::NotInitialized);
            }

            // Did we cache any triggers during the previous poll?
            if self.trigger_status[*pin as usize].triggered {
                self.trigger_status[*pin as usize].triggered = false;

                if !reset {
                    return Ok((*pin, self.trigger_status[*pin as usize].level));
                }
            }

            // Read the logic level to reset any pending trigger events
            if let Some(ref mut interrupt) = self.trigger_status[*pin as usize].interrupt {
                if reset {
                    interrupt.level()?;
                }
            }
        }

        // Loop until we get any of the events we're waiting for, or a timeout occurs
        loop {
            self.poll.poll(&mut self.events, timeout)?;

            // No events means a timeout occurred
            if self.events.is_empty() {
                return Err(Error::TimeOut);
            }

            for event in &self.events {
                if event.token().0 < self.trigger_status.capacity()
                    && event.readiness().is_readable()
                    && UnixReady::from(event.readiness()).is_error()
                {
                    self.trigger_status[event.token().0].triggered = true;
                    self.trigger_status[event.token().0].level = if let Some(ref mut interrupt) =
                        self.trigger_status[event.token().0].interrupt
                    {
                        interrupt.level()?
                    } else {
                        Level::Low
                    };
                }
            }

            // Were any interrupts triggered? If so, return one. The rest
            // will be saved for the next poll.
            for pin in pins {
                if self.trigger_status[*pin as usize].triggered {
                    self.trigger_status[*pin as usize].triggered = false;
                    return Ok((*pin, self.trigger_status[*pin as usize].level));
                }
            }

            // TODO: If there's a timeout set, reduce it here
        }
    }

    pub fn set_interrupt(&mut self, pin: u8, trigger: Trigger) -> Result<()> {
        self.trigger_status[pin as usize].triggered = false;

        // Interrupt already exists. We just need to change the trigger.
        if let Some(ref mut interrupt) = self.trigger_status[pin as usize].interrupt {
            if interrupt.trigger != trigger {
                interrupt.set_trigger(trigger)?;
            }

            return Ok(());
        }

        // Register a new interrupt
        let mut base = Interrupt::new(pin, trigger)?;

        base.level()?;
        self.poll.register(
            &base,
            Token(pin as usize),
            Ready::readable() | UnixReady::error(),
            PollOpt::edge(),
        )?;

        self.trigger_status[pin as usize].interrupt = Some(base);

        Ok(())
    }

    pub fn clear_interrupt(&mut self, pin: u8) -> Result<()> {
        self.trigger_status[pin as usize].triggered = false;

        if let Some(interrupt) = self.trigger_status[pin as usize].interrupt.take() {
            self.poll.deregister(&interrupt)?;
        }

        Ok(())
    }
}

enum ControlMsg {
    Stop,
}

pub struct AsyncInterrupt {
    pin: u8,
    poll_thread: Option<thread::JoinHandle<Result<()>>>,
    tx: channel::Sender<ControlMsg>,
}

impl AsyncInterrupt {
    pub fn new<C>(pin: u8, trigger: Trigger, mut callback: C) -> Result<AsyncInterrupt>
    where
        C: FnMut(Level) + Send + 'static,
    {
        let (tx, rx) = channel::channel();

        let poll_thread = thread::spawn(move || -> Result<()> {
            let poll = Poll::new()?;
            let mut events = Events::with_capacity(2);

            poll.register(&rx, Token(TOKEN_RX), Ready::readable(), PollOpt::edge())?;

            let mut base = Interrupt::new(pin, trigger)?;
            base.level()?;
            poll.register(
                &base,
                Token(TOKEN_PIN),
                Ready::readable() | UnixReady::error(),
                PollOpt::edge(),
            )?;

            loop {
                poll.poll(&mut events, None)?;

                for event in &events {
                    if event.token() == Token(TOKEN_RX) {
                        match rx.try_recv() {
                            Ok(ControlMsg::Stop) => {
                                return Ok(());
                            }
                            Err(TryRecvError::Disconnected) => {
                                return Ok(());
                            }
                            Err(TryRecvError::Empty) => {
                                break;
                            }
                        }
                    } else if event.token() == Token(TOKEN_PIN) && event.readiness().is_readable()
                        && UnixReady::from(event.readiness()).is_error()
                    {
                        let interrupt_value = base.level()?;

                        callback(interrupt_value);
                    }
                }
            }
        });

        Ok(AsyncInterrupt {
            pin,
            poll_thread: Some(poll_thread),
            tx,
        })
    }

    pub fn stop(&mut self) -> Result<()> {
        self.tx.send(ControlMsg::Stop)?;

        if let Some(poll_thread) = self.poll_thread.take() {
            match poll_thread.join() {
                Ok(r) => return r,
                Err(_) => return Err(Error::ThreadPanic),
            }
        }

        Ok(())
    }
}

impl Drop for AsyncInterrupt {
    fn drop(&mut self) {
        // Unexport the pin here, because we can't rely on the thread
        // living long enough to unexport it.
        sysfs::unexport(self.pin).ok();
    }
}
<commit_msg>Check elapsed time after each sync interrupt poll<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fs::File;
use std::io;
use std::io::{Read, Seek, SeekFrom};
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::mpsc::TryRecvError;
use std::thread;
use std::time::{Duration, Instant};

use mio::event::Evented;
use mio::unix::{EventedFd, UnixReady};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel;

use gpio::sysfs;

pub use gpio::sysfs::Direction;
pub use gpio::{Level, Trigger};

quick_error! {
    #[derive(Debug)]
/// Errors that can occur while working with interrupts.
    pub enum Error {
/// Synchronous interrupt isn't initialized.
        NotInitialized { description("not initialized") }
/// Time out.
        TimeOut { description("interrupt polling timed out while waiting for a trigger") }
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// Sysfs error.
        Sysfs(err: sysfs::Error) { description(err.description()) from() }
/// IO error while communicating with the interrupt polling thread.
        SendIo(err: io::Error) { description(err.description()) }
/// Disconnected while sending a control message to the interrupt polling thread.
        SendDisconnected { description("receiving half of the channel has disconnected") }
/// Interrupt polling thread panicked.
        ThreadPanic { description("interrupt polling thread panicked") }
    }
}

impl<T> From<channel::SendError<T>> for Error {
    fn from(err: channel::SendError<T>) -> Error {
        match err {
            channel::SendError::Io(e) => Error::SendIo(e),
            channel::SendError::Disconnected(_) => Error::SendDisconnected,
        }
    }
}

/// Result type returned from methods that can have `rppal::gpio::interrupt::Error`s.
pub type Result<T> = result::Result<T, Error>;

const TOKEN_RX: usize = 0;
const TOKEN_PIN: usize = 1;

struct Interrupt {
    pin: u8,
    trigger: Trigger,
    sysfs_value: File,
}

impl Interrupt {
    fn new(pin: u8, trigger: Trigger) -> Result<Interrupt> {
        // Export the GPIO pin so we can configure it through sysfs, set its mode to
        // input, and set the trigger type.
        sysfs::export(pin)?;
        sysfs::set_direction(pin, Direction::In)?;
        sysfs::set_edge(pin, trigger)?;

        Ok(Interrupt {
            pin,
            trigger,
            sysfs_value: sysfs::open_value(pin)?,
        })
    }

    fn trigger(&self) -> Trigger {
        self.trigger
    }

    fn set_trigger(&mut self, trigger: Trigger) -> Result<()> {
        self.trigger = trigger;
        sysfs::set_edge(self.pin, trigger)?;

        Ok(())
    }

    fn level(&mut self) -> Result<Level> {
        let mut buffer = [0; 1];
        self.sysfs_value.read_exact(&mut buffer)?;
        self.sysfs_value.seek(SeekFrom::Start(0))?;

        match &buffer {
            b"0" => Ok(Level::Low),
            _ => Ok(Level::High),
        }
    }
}

impl Drop for Interrupt {
    fn drop(&mut self) {
        sysfs::unexport(self.pin).ok();
    }
}

impl Evented for Interrupt {
    fn register(
        &self,
        poll: &Poll,
        token: Token,
        interest: Ready,
        opts: PollOpt,
    ) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).register(poll, token, interest, opts)
    }

    fn reregister(
        &self,
        poll: &Poll,
        token: Token,
        interest: Ready,
        opts: PollOpt,
    ) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &Poll) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).deregister(poll)
    }
}

struct TriggerStatus {
    interrupt: Option<Interrupt>,
    triggered: bool,
    level: Level,
}

pub struct EventLoop {
    poll: Poll,
    events: Events,
    trigger_status: Vec<TriggerStatus>,
}

impl EventLoop {
    pub fn new(capacity: usize) -> Result<EventLoop> {
        let mut trigger_status = Vec::with_capacity(capacity);

        // Initialize trigger_status while circumventing the Copy/Clone requirement
        for _ in 0..trigger_status.capacity() {
            trigger_status.push(TriggerStatus {
                interrupt: None,
                triggered: false,
                level: Level::Low,
            });
        }

        Ok(EventLoop {
            poll: Poll::new()?,
            events: Events::with_capacity(capacity),
            trigger_status,
        })
    }

    pub fn poll(
        &mut self,
        pins: &[u8],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<(u8, Level)> {
        for pin in pins {
            if *pin as usize >= self.trigger_status.capacity() {
                return Err(Error::NotInitialized);
            }

            // Did we cache any trigger events during the previous poll?
            if self.trigger_status[*pin as usize].triggered {
                self.trigger_status[*pin as usize].triggered = false;

                if !reset {
                    return Ok((*pin, self.trigger_status[*pin as usize].level));
                }
            }

            // Read the logic level to reset any pending trigger events
            if let Some(ref mut interrupt) = self.trigger_status[*pin as usize].interrupt {
                if reset {
                    interrupt.level()?;
                }
            }
        }

        // Loop until we get any of the events we're waiting for, or a timeout occurs
        let now = Instant::now();
        loop {
            self.poll.poll(&mut self.events, timeout)?;

            // No events means a timeout occurred
            if self.events.is_empty() {
                return Err(Error::TimeOut);
            }

            for event in &self.events {
                if event.token().0 < self.trigger_status.capacity()
                    && event.readiness().is_readable()
                    && UnixReady::from(event.readiness()).is_error()
                {
                    self.trigger_status[event.token().0].triggered = true;
                    self.trigger_status[event.token().0].level = if let Some(ref mut interrupt) =
                        self.trigger_status[event.token().0].interrupt
                    {
                        interrupt.level()?
                    } else {
                        Level::Low
                    };
                }
            }

            // Were any interrupts triggered? If so, return one. The rest
            // will be saved for the next poll.
            for pin in pins {
                if self.trigger_status[*pin as usize].triggered {
                    self.trigger_status[*pin as usize].triggered = false;
                    return Ok((*pin, self.trigger_status[*pin as usize].level));
                }
            }

            // It's possible a pin we're not waiting for continuously triggers
            // an interrupt, causing repeated loops with calls to poll() using a
            // reset timeout value. Make sure we haven't been looping longer than
            // the requested timeout.
            if let Some(t) = timeout {
                if now.elapsed() > t {
                    return Err(Error::TimeOut);
                }
            }
        }
    }

    pub fn set_interrupt(&mut self, pin: u8, trigger: Trigger) -> Result<()> {
        self.trigger_status[pin as usize].triggered = false;

        // Interrupt already exists. We just need to change the trigger.
        if let Some(ref mut interrupt) = self.trigger_status[pin as usize].interrupt {
            if interrupt.trigger != trigger {
                interrupt.set_trigger(trigger)?;
            }

            return Ok(());
        }

        // Register a new interrupt
        let mut base = Interrupt::new(pin, trigger)?;

        base.level()?;
        self.poll.register(
            &base,
            Token(pin as usize),
            Ready::readable() | UnixReady::error(),
            PollOpt::edge(),
        )?;

        self.trigger_status[pin as usize].interrupt = Some(base);

        Ok(())
    }

    pub fn clear_interrupt(&mut self, pin: u8) -> Result<()> {
        self.trigger_status[pin as usize].triggered = false;

        if let Some(interrupt) = self.trigger_status[pin as usize].interrupt.take() {
            self.poll.deregister(&interrupt)?;
        }

        Ok(())
    }
}

enum ControlMsg {
    Stop,
}

pub struct AsyncInterrupt {
    pin: u8,
    poll_thread: Option<thread::JoinHandle<Result<()>>>,
    tx: channel::Sender<ControlMsg>,
}

impl AsyncInterrupt {
    pub fn new<C>(pin: u8, trigger: Trigger, mut callback: C) -> Result<AsyncInterrupt>
    where
        C: FnMut(Level) + Send + 'static,
    {
        let (tx, rx) = channel::channel();

        let poll_thread = thread::spawn(move || -> Result<()> {
            let poll = Poll::new()?;
            let mut events = Events::with_capacity(2);

            poll.register(&rx, Token(TOKEN_RX), Ready::readable(), PollOpt::edge())?;

            let mut base = Interrupt::new(pin, trigger)?;
            base.level()?;
            poll.register(
                &base,
                Token(TOKEN_PIN),
                Ready::readable() | UnixReady::error(),
                PollOpt::edge(),
            )?;

            loop {
                poll.poll(&mut events, None)?;

                for event in &events {
                    if event.token() == Token(TOKEN_RX) {
                        match rx.try_recv() {
                            Ok(ControlMsg::Stop) => {
                                return Ok(());
                            }
                            Err(TryRecvError::Disconnected) => {
                                return Ok(());
                            }
                            Err(TryRecvError::Empty) => {
                                break;
                            }
                        }
                    } else if event.token() == Token(TOKEN_PIN)
                        && event.readiness().is_readable()
                        && UnixReady::from(event.readiness()).is_error()
                    {
                        let interrupt_value = base.level()?;

                        callback(interrupt_value);
                    }
                }
            }
        });

        Ok(AsyncInterrupt {
            pin,
            poll_thread: Some(poll_thread),
            tx,
        })
    }

    pub fn stop(&mut self) -> Result<()> {
        self.tx.send(ControlMsg::Stop)?;

        if let Some(poll_thread) = self.poll_thread.take() {
            match poll_thread.join() {
                Ok(r) => return r,
                Err(_) => return Err(Error::ThreadPanic),
            }
        }

        Ok(())
    }
}

impl Drop for AsyncInterrupt {
    fn drop(&mut self) {
        // Unexport the pin here, because we can't rely on the thread
        // living long enough to unexport it.
        sysfs::unexport(self.pin).ok();
    }
}
<|endoftext|>


<commit_before>/*================================================================================================*/
// Copyright 2016 Kyle Finlay
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*================================================================================================*/

use ::util::{Logger, Version};
use ::engine::PluginManager;

use std::cell::RefCell;
use std::rc::Rc;

/*================================================================================================*/
/*------APP STRUCT--------------------------------------------------------------------------------*/
/*================================================================================================*/

/// The app
///
/// This is the main control center of ionCore.
/// It is in charge of initialization, updating, and shutdown of all modules,
/// as well as the handing of any inter-module communication.
#[derive (Clone)]
pub struct App {

    // Public
    /// Manages all plugins.
    pub plugin_manager: PluginManager,

    // Private
    _name: String,
    _developer: String,
    _version: Version,
    _self_ref: Option<Rc<RefCell<App>>>
}

/*================================================================================================*/
/*------APPVERSION PUBLIC METHODS-----------------------------------------------------------------*/
/*================================================================================================*/

impl App {

    /// Returns the name of the application.
    ///
    /// # Return value
    /// An immutable reference to the app name.
    pub fn get_name (&self) -> &str {
        &self._name
    }

    /// Returns the application developer
    ///
    /// # Return value
    /// An immutable reference to the app developer
    pub fn get_developer (&self) -> &str {
        &self._developer
    }

    /// Returns the version of the application.
    ///
    /// # Return value
    /// An immutable reference to the app version.
    pub fn get_version (&self) -> &Version {
        &self._version
    }

/*================================================================================================*/
/*------APPVERSION PUBLIC STATIC METHODS----------------------------------------------------------*/
/*================================================================================================*/

    /// Returns a new instance of the app builder
    ///
    /// # Return value
    /// A new instance of the app builder.
    pub fn builder () -> AppBuilder {

        AppBuilder {_name: "Ion App".to_string (),
                    _developer: String::new (),
                    _version: Version {major: 0, minor: 1, patch: 0}}
    }
}

/*================================================================================================*/
/*------APPBUILDER STRUCT-------------------------------------------------------------------------*/
/*================================================================================================*/

/// The app builder
///
/// The app builder provides an easy and straightforward way of creating and
/// configuring a new app instance.
#[derive (Clone)]
pub struct AppBuilder {

    // Private
    _name: String,
    _developer: String,
    _version: Version
}

/*================================================================================================*/
/*------APPBUILDER PUBLIC METHODS-----------------------------------------------------------------*/
/*================================================================================================*/

impl AppBuilder {

    /// Builds the application
    ///
    /// # Return value
    /// A new app instance.
    pub fn build (&self) -> App {

        Logger::init ("./ionCore.log", true).unwrap ();
        info! ("Initializing ionCore | Version: {}", env! ("CARGO_PKG_VERSION"));

        App {plugin_manager: PluginManager::new (),
             _name: self._name.clone (),
             _developer: self._developer.clone (),
             _version: self._version,
             _self_ref: None}
    }

    /// Sets the application name.
    ///
    /// # Arguments
    /// * `name` - The desired name of the application.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn name (&mut self, name: &str) -> &mut AppBuilder {

        self._name = name.to_string ();
        self
    }

    /// Sets the application developer.
    ///
    /// # Arguments
    /// * `developer` - The name of the developer.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn developer (&mut self, developer: &str) -> &mut AppBuilder {

        self._developer = developer.to_string ();
        self
    }

    /// Sets the version of the app.
    ///
    /// # Arguments
    /// * 'major' - The major version of the app.
    /// * `minor` - The minor version of the app.
    /// * `patch` - The patch version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version (&mut self, major: i32, minor: i32, patch: i32) -> &mut AppBuilder {

        self._version.major = major;
        self._version.minor = minor;
        self._version.patch = patch;

        self
    }

    /// Sets the major version of the app.
    ///
    /// # Arguments
    /// * `major` - The major version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version_major (&mut self, major: i32) -> &mut AppBuilder {

        self._version.major = major;
        self
    }

    /// Sets the minor version of the app.
    ///
    /// # Arguments
    /// * `minor` - The minor version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version_minor (&mut self, minor: i32) -> &mut AppBuilder {

        self._version.minor = minor;
        self
    }

    /// Sets the patch version of the app.
    ///
    /// # Arguments
    /// * `patch` - The patch version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version_patch (&mut self, patch: i32) -> &mut AppBuilder {

        self._version.patch = patch;
        self
    }
}
<commit_msg>App is now stored statically on creation<commit_after>/*================================================================================================*/
// Copyright 2016 Kyle Finlay
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*================================================================================================*/

use ::util::{Logger, Version};
use ::engine::PluginManager;

use std::boxed::Box;

/*================================================================================================*/
/*------STATIC VARIABLES--------------------------------------------------------------------------*/
/*================================================================================================*/

static mut APP_POINTER: Option <*mut App> = None;

/*================================================================================================*/
/*------APP STRUCT--------------------------------------------------------------------------------*/
/*================================================================================================*/

/// The app
///
/// This is the main control center of ionCore.
/// It is in charge of initialization, updating, and shutdown of all modules,
/// as well as the handing of any inter-module communication.
#[derive (Clone)]
pub struct App {

    // Public
    /// Manages all plugins.
    pub plugin_manager: PluginManager,

    // Private
    _name: String,
    _developer: String,
    _version: Version
}

/*================================================================================================*/
/*------APPVERSION PUBLIC METHODS-----------------------------------------------------------------*/
/*================================================================================================*/

impl App {

    /// Returns the name of the application.
    ///
    /// # Return value
    /// An immutable reference to the app name.
    pub fn get_name (&self) -> &str {
        &self._name
    }

    /// Returns the application developer
    ///
    /// # Return value
    /// An immutable reference to the app developer
    pub fn get_developer (&self) -> &str {
        &self._developer
    }

    /// Returns the version of the application.
    ///
    /// # Return value
    /// An immutable reference to the app version.
    pub fn get_version (&self) -> &Version {
        &self._version
    }

/*================================================================================================*/
/*------APPVERSION PUBLIC STATIC METHODS----------------------------------------------------------*/
/*================================================================================================*/

    /// Returns a new instance of the app builder
    ///
    /// # Return value
    /// A new instance of the app builder.
    pub fn builder () -> AppBuilder {

        AppBuilder {_name: "Ion App".to_string (),
                    _developer: String::new (),
                    _version: Version {major: 0, minor: 1, patch: 0}}
    }

    /// Gets a reference to the current app instance.
    pub fn instance () -> &'static App {
        unsafe {&*APP_POINTER.unwrap ()}
    }

    /// Releases the app instance.
    pub fn release () {

        info! ("Shutting down ion Core
        Using 'App' after this point will result in a panic.");

        unsafe {

            drop (Box::from_raw (APP_POINTER.unwrap ()));
            APP_POINTER = None;
        };
    }
}

/*================================================================================================*/
/*------APPBUILDER STRUCT-------------------------------------------------------------------------*/
/*================================================================================================*/

/// The app builder
///
/// The app builder provides an easy and straightforward way of creating and
/// configuring a new app instance.
#[derive (Clone)]
pub struct AppBuilder {

    // Private
    _name: String,
    _developer: String,
    _version: Version
}

/*================================================================================================*/
/*------APPBUILDER PUBLIC METHODS-----------------------------------------------------------------*/
/*================================================================================================*/

impl AppBuilder {

    /// Builds the application
    ///
    /// # Return value
    /// A new app instance.
    pub fn build (&self) -> &'static App  {

        Logger::init ("./ionCore.log", true).unwrap ();
        info! ("Initializing ionCore | Version: {}", env! ("CARGO_PKG_VERSION"));

        let app_box = Box::new (App {plugin_manager: PluginManager::new (),
                                     _name: self._name.clone (),
                                     _developer: self._developer.clone (),
                                     _version: self._version});

        unsafe {APP_POINTER = Some (Box::into_raw (app_box))};
        App::instance ()
    }

    /// Sets the application name.
    ///
    /// # Arguments
    /// * `name` - The desired name of the application.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn name (&mut self, name: &str) -> &mut AppBuilder {

        self._name = name.to_string ();
        self
    }

    /// Sets the application developer.
    ///
    /// # Arguments
    /// * `developer` - The name of the developer.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn developer (&mut self, developer: &str) -> &mut AppBuilder {

        self._developer = developer.to_string ();
        self
    }

    /// Sets the version of the app.
    ///
    /// # Arguments
    /// * 'major' - The major version of the app.
    /// * `minor` - The minor version of the app.
    /// * `patch` - The patch version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version (&mut self, major: i32, minor: i32, patch: i32) -> &mut AppBuilder {

        self._version.major = major;
        self._version.minor = minor;
        self._version.patch = patch;

        self
    }

    /// Sets the major version of the app.
    ///
    /// # Arguments
    /// * `major` - The major version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version_major (&mut self, major: i32) -> &mut AppBuilder {

        self._version.major = major;
        self
    }

    /// Sets the minor version of the app.
    ///
    /// # Arguments
    /// * `minor` - The minor version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version_minor (&mut self, minor: i32) -> &mut AppBuilder {

        self._version.minor = minor;
        self
    }

    /// Sets the patch version of the app.
    ///
    /// # Arguments
    /// * `patch` - The patch version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version_patch (&mut self, patch: i32) -> &mut AppBuilder {

        self._version.patch = patch;
        self
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate bencher;
extern crate rand;
extern crate ropey;

use bencher::Bencher;
use rand::random;
use ropey::Rope;

const TEXT: &str = include_str!("large.txt");

fn mul_string_length(text: &str, n: usize) -> String {
    let mut mtext = String::new();
    for _ in 0..n {
        mtext.push_str(text);
    }
    mtext
}

//----
const LEN_MUL_SMALL: usize = 1;

fn removals_random_small(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_SMALL);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = random::<usize>() % (len + 1);
        let end = (start + 1).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_start_small(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_SMALL);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = 0;
        let end = (start + 1).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_middle_small(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_SMALL);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = len / 2;
        let end = (start + 1).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_end_small(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_SMALL);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let end = len;
        let start = end - (1).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

//----
const LEN_MUL_MEDIUM: usize = 1;

fn removals_random_medium(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_MEDIUM);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = random::<usize>() % (len + 1);
        let end = (start + 15).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_start_medium(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_MEDIUM);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = 0;
        let end = (start + 15).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_middle_medium(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_MEDIUM);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = len / 2;
        let end = (start + 15).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_end_medium(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_MEDIUM);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let end = len;
        let start = end - (15).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

//----

const LEN_MUL_LARGE: usize = 4;

fn removals_random_large(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_LARGE);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = random::<usize>() % (len + 1);
        let end = (start + 1500).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == 0 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_start_large(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_LARGE);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = 0;
        let end = (start + 1500).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == 0 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_middle_large(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_LARGE);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = len / 2;
        let end = (start + 1500).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == 0 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_end_large(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_LARGE);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let end = len;
        let start = end - (1500).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == 0 {
            rope = Rope::from_str(&text);
        }
    })
}

//----

benchmark_group!(
    benches,
    removals_random_small,
    removals_start_small,
    removals_middle_small,
    removals_end_small,
    removals_random_medium,
    removals_start_medium,
    removals_middle_medium,
    removals_end_medium,
    removals_random_large,
    removals_start_large,
    removals_middle_large,
    removals_end_large
);
benchmark_main!(benches);
<commit_msg>Another tweak to the Rope::remove() benchmarks.<commit_after>#[macro_use]
extern crate bencher;
extern crate rand;
extern crate ropey;

use bencher::Bencher;
use rand::random;
use ropey::Rope;

const TEXT: &str = include_str!("large.txt");
const TEXT_SMALL: &str = include_str!("small.txt");

fn mul_string_length(text: &str, n: usize) -> String {
    let mut mtext = String::new();
    for _ in 0..n {
        mtext.push_str(text);
    }
    mtext
}

//----
const LEN_MUL_SMALL: usize = 1;

fn removals_random_small(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_SMALL);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = random::<usize>() % (len + 1);
        let end = (start + 1).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_start_small(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_SMALL);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = 0;
        let end = (start + 1).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_middle_small(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_SMALL);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = len / 2;
        let end = (start + 1).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_end_small(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_SMALL);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let end = len;
        let start = end - (1).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

//----
const LEN_MUL_MEDIUM: usize = 1;

fn removals_random_medium(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_MEDIUM);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = random::<usize>() % (len + 1);
        let end = (start + 15).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_start_medium(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_MEDIUM);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = 0;
        let end = (start + 15).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_middle_medium(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_MEDIUM);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = len / 2;
        let end = (start + 15).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_end_medium(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_MEDIUM);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let end = len;
        let start = end - (15).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == TEXT.len() / 2 {
            rope = Rope::from_str(&text);
        }
    })
}

//----

const LEN_MUL_LARGE: usize = 4;

fn removals_random_large(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_LARGE);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = random::<usize>() % (len + 1);
        let end = (start + TEXT_SMALL.len()).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == 0 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_start_large(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_LARGE);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = 0;
        let end = (start + TEXT_SMALL.len()).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == 0 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_middle_large(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_LARGE);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let start = len / 2;
        let end = (start + TEXT_SMALL.len()).min(len);
        rope.remove(start..end);

        if rope.len_bytes() == 0 {
            rope = Rope::from_str(&text);
        }
    })
}

fn removals_end_large(bench: &mut Bencher) {
    let text = mul_string_length(TEXT, LEN_MUL_LARGE);
    let mut rope = Rope::from_str(&text);

    bench.iter(|| {
        let len = rope.len_chars();
        let end = len;
        let start = end - TEXT_SMALL.len().min(len);
        rope.remove(start..end);

        if rope.len_bytes() == 0 {
            rope = Rope::from_str(&text);
        }
    })
}

//----

benchmark_group!(
    benches,
    removals_random_small,
    removals_start_small,
    removals_middle_small,
    removals_end_small,
    removals_random_medium,
    removals_start_medium,
    removals_middle_medium,
    removals_end_medium,
    removals_random_large,
    removals_start_large,
    removals_middle_large,
    removals_end_large
);
benchmark_main!(benches);
<|endoftext|>