<commit_before>//! Promises in Rust.
//! See the `Promise` struct for more details.

#![warn(missing_docs)]

#[cfg(test)]
mod tests;

use std::thread;
use std::sync::mpsc::channel;
//use std::thread::JoinHandle;
use std::marker::{Send};
use std::sync::mpsc::{Sender, Receiver, TryRecvError};

/// A promise is a way of doing work in the background. The promises in
/// this library have the same featureset as those in Ecmascript 5.
///
/// # Promises
/// Promises (sometimes known as "futures") are objects that represent
/// asynchronous tasks being run in the background, or results which
/// will exist in the future.
/// A promise will be in state of running, fulfilled, rejected, or done.
/// In order to use the results of a fulfilled promise, one attaches another
/// promise to it (i.e. via `then`). Like their Javascript counterparts,
/// promises can return an error (of type `E`). Unlike Javascript, success or /// failure is indicated by a `Result` type. This is much more Rustic and
/// allows existing functions which return a `Result<T, E>` to be used.
///
/// # Panics
/// If the function being executed by a promise panics, it does so silently.
/// The panic will not resurface in the thread which created the promise,
/// and promises waiting on its result will never be called. In addition,
/// the `all` and `race` proimse methods will _ignore_ "dead" promises. They
/// will remove promises from their lists, and if there aren't any left
/// they will silently exit without doing anything.
///
/// Unfortunately, panics must be ignored for two reasons:
/// * Panic messages don't have a concrete type yet in Rust. If they did,
/// promiess would be able to inspect their predecessors' errors.
/// * Although a `Receiver` can correctly handle its paired `Sender` being
/// dropped, such as during a panic, for reasons stated above the "message"
/// of the panic is not relayed.
///
/// Finally, Ecmascript promises themselves do have the ability to return
/// and error type, represented as a `Result<T, E>` here. Thus, one should
/// use `try!` and other error handling rather than calls to `unwrap()`.
pub struct Promise<T: Send, E: Send> {
    receiver: Receiver<Result<T, E>>
}

impl<T: Send + 'static, E: Send + 'static> Promise<T, E> {

    /// Chains a function to be called after this promise resolves.
    pub fn then<T2, E2, F1, F2>(self, callback: F1, errback: F2)
                                -> Promise<T2, E2>
    where T2: Send + 'static, E2: Send + 'static,
    F1: FnOnce(T) -> Result<T2, E2>, F2: FnOnce(E) -> Result<T2, E2>,
    F1: Send + 'static, F2: Send + 'static {
        let recv = self.receiver;
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_then(tx, recv, callback, errback);
        });

        Promise { receiver: rx }
    }

    /// Chains a function to be called after this promise resolves,
    /// using a `Result` type.
    pub fn then_result<T2, E2, F>(self, callback: F) -> Promise<T2, E2>
    where T2: Send + 'static, E2: Send + 'static,
    F: FnOnce(Result<T, E>) -> Result<T2, E2>, F: Send + 'static {
        let recv = self.receiver;
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_then_result(tx, recv, callback);
        });

        Promise { receiver: rx }
    }

    /// Creates a new promsie, which will eventually resolve to one of the
    /// values of the `Result<T, E>` type.
    pub fn new<F>(func: F) -> Promise<T, E>
    where F: FnOnce() -> Result<T, E>, F: Send + 'static {
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_new(tx, func);
        });

        Promise { receiver: rx }
    }

    /// Applies a promise to the first of some promises to become fulfilled.
    pub fn race(promises: Vec<Promise<T, E>>) -> Promise<T, E> {
        let recs = promises.into_iter().map(|p| p.receiver).collect();
        let (tx, rx) = channel::<Result<T, E>>();

        thread::spawn(move || {
            Promise::impl_race(tx, recs);
        });

        Promise { receiver: rx }
    }

    /// Calls a function with the result of all of the promises, or the error
    /// of the first promise to error.
    pub fn all(promises: Vec<Promise<T, E>>) -> Promise<Vec<T>, E> {
        let receivers: Vec<Receiver<Result<T, E>>> =
            promises.into_iter().map(|p| p.receiver).collect();
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_all(tx, receivers);
        });

        return Promise { receiver: rx };
    }

    /// Creates a promise that resolves to a value
    pub fn resolve(val: T) -> Promise<T, E> {
        Promise::from_result(Ok(val))
    }

    /// Creates a promise that resolves to an error.
    pub fn reject(val: E) -> Promise<T, E> {
        Promise::from_result(Err(val))
    }

    /// Creates a new promise that will resolve to the result value.
    pub fn from_result(result: Result<T, E>) -> Promise<T, E> {
        let (tx, rx) = channel();
        tx.send(result).unwrap();

        Promise { receiver: rx }
    }

    // Implementation Functions

    fn impl_new<F>(tx: Sender<Result<T, E>>, func: F)
    where F: FnOnce() -> Result<T, E>, F: Send + 'static {
        let result = func();
        tx.send(result).unwrap_or(());
    }

    fn impl_then<T2, E2, F1, F2>(tx: Sender<Result<T2, E2>>,
                                 rx: Receiver<Result<T, E>>,
                                 callback: F1, errback: F2)
    where T2: Send + 'static, E2: Send + 'static,
    F1: FnOnce(T) -> Result<T2, E2>, F2: FnOnce(E) -> Result<T2, E2>,
    F1: Send + 'static, F2: Send + 'static
    {
        if let Ok(message) = rx.recv() {
            match message {
                Ok(val) => tx.send(callback(val)).unwrap_or(()),
                Err(err) => tx.send(errback(err)).unwrap_or(())
            };
        }
    }

    fn impl_then_result<T2, E2, F>(tx: Sender<Result<T2, E2>>,
                                    rx: Receiver<Result<T, E>>,
                                    callback: F)
    where T2: Send + 'static, E2: Send + 'static,
    F: FnOnce(Result<T, E>) -> Result<T2, E2>, F: Send + 'static {

        if let Ok(result) = rx.recv() {
            tx.send(callback(result)).unwrap_or(());
        }
    }

    // Static methods

    fn impl_race(tx: Sender<Result<T, E>>,
                 mut recs: Vec<Receiver<Result<T, E>>>) {
        'outer: loop {
            // Don't get stuck in an infinite loop
            if recs.len() == 0 { return; }
            for i in 0..recs.len() {
                match recs[i].try_recv() {
                    Ok(val) => {
                        tx.send(val).unwrap_or(());
                        return;
                    }
                    Err(err) => {
                        if err == TryRecvError::Disconnected {
                            recs.remove(i);
                        }
                    }
                }
            }
        }
    }

    fn impl_all(tx: Sender<Result<Vec<T>, E>>,
                recs: Vec<Receiver<Result<T, E>>>) {
        let mut values: Vec<T> = Vec::with_capacity(recs.len());
        let mut mut_receivers = recs;
        'outer: loop {
            for i in 0..mut_receivers.len() {
                match mut_receivers[i].try_recv() {
                    Ok(val) => {
                        match val {
                            Ok(t) => values.push(t),
                            Err(e) => {
                                tx.send(Err(e)).unwrap_or(());
                                return;
                            }
                        }
                        mut_receivers.remove(i);
                    }
                    Err(err) => {
                        if err == TryRecvError::Disconnected {
                            mut_receivers.remove(i);
                        }
                    }
                }
            }
            // Check if we are finished waiting for promises
            // This can also happen if all promises panic
            if mut_receivers.len() == 0 {
                let result = Ok(values);
                tx.send(result).unwrap_or(());
                return; // Break from outer loop
            }
        }
    }
}
<commit_msg>Add ok_then and err_then methods<commit_after>//! Promises in Rust.
//! See the `Promise` struct for more details.

#![warn(missing_docs)]

#[cfg(test)]
mod tests;

use std::thread;
use std::sync::mpsc::channel;
//use std::thread::JoinHandle;
use std::marker::{Send};
use std::sync::mpsc::{Sender, Receiver, TryRecvError};

/// A promise is a way of doing work in the background. The promises in
/// this library have the same featureset as those in Ecmascript 5.
///
/// # Promises
/// Promises (sometimes known as "futures") are objects that represent
/// asynchronous tasks being run in the background, or results which
/// will exist in the future.
/// A promise will be in state of running, fulfilled, rejected, or done.
/// In order to use the results of a fulfilled promise, one attaches another
/// promise to it (i.e. via `then`). Like their Javascript counterparts,
/// promises can return an error (of type `E`). Unlike Javascript, success or
/// failure is indicated by a `Result` type. This is much more Rustic and
/// allows existing functions which return a `Result<T, E>` to be used.
///
/// # Panics
/// If the function being executed by a promise panics, it does so silently.
/// The panic will not resurface in the thread which created the promise,
/// and promises waiting on its result will never be called. In addition,
/// the `all` and `race` proimse methods will _ignore_ "dead" promises. They
/// will remove promises from their lists, and if there aren't any left
/// they will silently exit without doing anything.
///
/// Unfortunately, panics must be ignored for two reasons:
/// * Panic messages don't have a concrete type yet in Rust. If they did,
/// promiess would be able to inspect their predecessors' errors.
/// * Although a `Receiver` can correctly handle its paired `Sender` being
/// dropped, such as during a panic, for reasons stated above the "message"
/// of the panic is not relayed.
///
/// Finally, Ecmascript promises themselves do have the ability to return
/// and error type, represented as a `Result<T, E>` here. Thus, one should
/// use `try!` and other error handling rather than calls to `unwrap()`.
pub struct Promise<T: Send, E: Send> {
    receiver: Receiver<Result<T, E>>
}

impl<T: Send + 'static, E: Send + 'static> Promise<T, E> {

    /// Chains a function to be called after this promise resolves.
    ///
    ///
    pub fn then<T2, E2, F1, F2>(self, callback: F1, errback: F2)
                                -> Promise<T2, E2>
    where T2: Send + 'static, E2: Send + 'static,
    F1: FnOnce(T) -> Result<T2, E2>, F2: FnOnce(E) -> Result<T2, E2>,
    F1: Send + 'static, F2: Send + 'static {
        let recv = self.receiver;
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_then(tx, recv, callback, errback);
        });

        Promise { receiver: rx }
    }

    /// Chains a function to be called after this promise resolves,
    /// using a `Result` type.
    pub fn then_result<T2, E2, F>(self, callback: F) -> Promise<T2, E2>
    where T2: Send + 'static, E2: Send + 'static,
    F: FnOnce(Result<T, E>) -> Result<T2, E2>, F: Send + 'static {
        let recv = self.receiver;
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_then_result(tx, recv, callback);
        });

        Promise { receiver: rx }
    }

    /// Calls a function on the result of the promise if it is fulfilled.
    ///
    /// This is equivalent to a Javascript promise's `then` with one
    /// callback, or Rust's `Result::map`.
    pub fn ok_then<T2, F>(self, callback: F) -> Promise<T2, E>
    where T2: Send + 'static, F: Send + 'static,
    F: FnOnce(T) -> Result<T2, E> {
        let recv = self.receiver;
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_ok_then(tx, recv, callback);
        });

        Promise { receiver: rx }
    }

    /// Calls a function of the result of the promise if it fails.
    ///
    /// This is equivalent to Javascript promise's `catch`.
    pub fn err_then<E2, F>(self, errback: F) -> Promise<T, E2>
    where F: FnOnce(E) -> Result<T, E2>, F: Send + 'static,
    E2: Send + 'static {
        let recv = self.receiver;
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_err_then(tx, recv, errback);
        });

        Promise { receiver: rx }
    }

    /// Creates a new promsie, which will eventually resolve to one of the
    /// values of the `Result<T, E>` type.
    pub fn new<F>(func: F) -> Promise<T, E>
    where F: FnOnce() -> Result<T, E>, F: Send + 'static {
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_new(tx, func);
        });

        Promise { receiver: rx }
    }

    /// Applies a promise to the first of some promises to become fulfilled.
    pub fn race(promises: Vec<Promise<T, E>>) -> Promise<T, E> {
        let recs = promises.into_iter().map(|p| p.receiver).collect();
        let (tx, rx) = channel::<Result<T, E>>();

        thread::spawn(move || {
            Promise::impl_race(tx, recs);
        });

        Promise { receiver: rx }
    }

    /// Calls a function with the result of all of the promises, or the error
    /// of the first promise to error.
    pub fn all(promises: Vec<Promise<T, E>>) -> Promise<Vec<T>, E> {
        let receivers: Vec<Receiver<Result<T, E>>> =
            promises.into_iter().map(|p| p.receiver).collect();
        let (tx, rx) = channel();

        thread::spawn(move || {
            Promise::impl_all(tx, receivers);
        });

        return Promise { receiver: rx };
    }

    /// Creates a promise that resolves to a value
    pub fn resolve(val: T) -> Promise<T, E> {
        Promise::from_result(Ok(val))
    }

    /// Creates a promise that resolves to an error.
    pub fn reject(val: E) -> Promise<T, E> {
        Promise::from_result(Err(val))
    }

    /// Creates a new promise that will resolve to the result value.
    pub fn from_result(result: Result<T, E>) -> Promise<T, E> {
        let (tx, rx) = channel();
        tx.send(result).unwrap();

        Promise { receiver: rx }
    }

    // Implementation Functions

    fn impl_new<F>(tx: Sender<Result<T, E>>, func: F)
    where F: FnOnce() -> Result<T, E>, F: Send + 'static {
        let result = func();
        tx.send(result).unwrap_or(());
    }

    fn impl_then<T2, E2, F1, F2>(tx: Sender<Result<T2, E2>>,
                                 rx: Receiver<Result<T, E>>,
                                 callback: F1, errback: F2)
    where T2: Send + 'static, E2: Send + 'static,
    F1: FnOnce(T) -> Result<T2, E2>, F2: FnOnce(E) -> Result<T2, E2>,
    F1: Send + 'static, F2: Send + 'static
    {
        if let Ok(message) = rx.recv() {
            match message {
                Ok(val) => tx.send(callback(val)).unwrap_or(()),
                Err(err) => tx.send(errback(err)).unwrap_or(())
            };
        }
    }

    fn impl_then_result<T2, E2, F>(tx: Sender<Result<T2, E2>>,
                                    rx: Receiver<Result<T, E>>,
                                    callback: F)
    where T2: Send + 'static, E2: Send + 'static,
    F: FnOnce(Result<T, E>) -> Result<T2, E2>, F: Send + 'static {

        if let Ok(result) = rx.recv() {
            tx.send(callback(result)).unwrap_or(());
        }
    }

    fn impl_ok_then<T2, F>(tx: Sender<Result<T2, E>>,
                           rx: Receiver<Result<T, E>>, callback: F)
    where F: FnOnce(T) -> Result<T2, E>, F: Send + 'static,
    T2: Send + 'static {
        if let Ok(message) = rx.recv() {
            match message {
                Ok(val) => tx.send(callback(val)).unwrap_or(()),
                Err(err) => tx.send(Err(err)).unwrap_or(())
            }
        }
    }

    fn impl_err_then<E2, F>(tx: Sender<Result<T, E2>>,
                           rx: Receiver<Result<T, E>>, errback: F)
    where F: FnOnce(E) -> Result<T, E2>, F: Send + 'static,
    E2: Send + 'static {
        if let Ok(message) = rx.recv() {
            match message {
                Ok(val) => tx.send(Ok(val)).unwrap_or(()),
                Err(err) => tx.send(errback(err)).unwrap_or(())
            }
        }
    }

    // Static methods

    fn impl_race(tx: Sender<Result<T, E>>,
                 mut recs: Vec<Receiver<Result<T, E>>>) {
        'outer: loop {
            // Don't get stuck in an infinite loop
            if recs.len() == 0 { return; }
            for i in 0..recs.len() {
                match recs[i].try_recv() {
                    Ok(val) => {
                        tx.send(val).unwrap_or(());
                        return;
                    }
                    Err(err) => {
                        if err == TryRecvError::Disconnected {
                            recs.remove(i);
                        }
                    }
                }
            }
        }
    }

    fn impl_all(tx: Sender<Result<Vec<T>, E>>,
                recs: Vec<Receiver<Result<T, E>>>) {
        let mut values: Vec<T> = Vec::with_capacity(recs.len());
        let mut mut_receivers = recs;
        'outer: loop {
            for i in 0..mut_receivers.len() {
                match mut_receivers[i].try_recv() {
                    Ok(val) => {
                        match val {
                            Ok(t) => values.push(t),
                            Err(e) => {
                                tx.send(Err(e)).unwrap_or(());
                                return;
                            }
                        }
                        mut_receivers.remove(i);
                    }
                    Err(err) => {
                        if err == TryRecvError::Disconnected {
                            mut_receivers.remove(i);
                        }
                    }
                }
            }
            // Check if we are finished waiting for promises
            // This can also happen if all promises panic
            if mut_receivers.len() == 0 {
                let result = Ok(values);
                tx.send(result).unwrap_or(());
                return; // Break from outer loop
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Logging module

extern crate time;
extern crate libc;
use self::libc::{c_void};

use std::collections::vec_deque::VecDeque;
use std::fmt;
use std::io::{Write, Read, Seek, BufRead};
use std;

use utils;

pub enum FilterLog {
    None,
    Last(time::Tm)
}

impl FilterLog {
    ///Checks if given element is within allowed time range
    #[inline]
    pub fn check(&self, time: &time::Tm) -> bool {
        match *self {
            FilterLog::None => true,
            FilterLog::Last(from) => from < *time,
        }
    }
}

impl fmt::Display for FilterLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            FilterLog::None => write!(f, "None"),
            FilterLog::Last(from) => write!(f, "Last({})", from.strftime("%x %X.%f").unwrap()),
        }
    }
}

pub struct IrcLog {
    inner: VecDeque<IrcEntry>,
    fs_buf: std::fs::File
}

impl IrcLog {
    /// Creates log with default capacity 500.
    #[inline(always)]
    pub fn new() -> IrcLog {
        IrcLog {
            inner: VecDeque::with_capacity(500),
            //Open file log once for both write/read.
            //To read/write correctly be sure to .seek() at needed position
            fs_buf: std::fs::OpenOptions::new().read(true)
                                               .write(true)
                                               .create(true)
                                               .open("vndis.log")
                                               .unwrap()
        }
    }

    ///Dumps all logs except for last 20
    fn buff_to_file(&mut self) {
        let len = self.inner.len();

        if len <= 20 {return;}

        self.fs_buf.seek(std::io::SeekFrom::End(0)).unwrap();
        //range is exclusive at the end
        for _ in 0..len-19 {
            self.fs_buf.write_fmt(format_args!("{}\n", self.inner.pop_front().unwrap())).unwrap();
        }
        self.fs_buf.flush().unwrap()
    }

    ///Dumps all logs except for last 20
    fn dump_to_file(&mut self) {
        let len = self.inner.len();

        if len == 0 {return;}

        self.fs_buf.seek(std::io::SeekFrom::End(0)).unwrap();
        //range is exclusive at the end
        for _ in 0..len {
            self.fs_buf.write_fmt(format_args!("{}\n", self.inner.pop_front().unwrap())).unwrap();
        }
        self.fs_buf.flush().unwrap()
    }

    #[inline(always)]
    /// Adds entry to log.
    pub fn add(&mut self, entry: IrcEntry) {
        if self.len() >= self.capacity() {
            self.buff_to_file();
        }
        self.inner.push_back(entry);
    }

    #[inline]
    /// Reads all/filtered entries from underlying file buffer.
    pub fn fs_read(&mut self, filter: &FilterLog) -> String {
        if self.fs_buf.metadata().unwrap().len() == 0 {
            return "".to_string();
        }

        self.fs_buf.seek(std::io::SeekFrom::Start(0)).unwrap();

        let reader = std::io::BufReader::new(&mut self.fs_buf);
        let lines = reader.lines();

        //Let's try to peek the number of lines for effective allocation
        let acc_str = if let (_, Some(len)) = lines.size_hint() {
            String::with_capacity(len*50)
        }
        else {
            String::new()
        };

        lines.fold(acc_str, |acc, line| {
            const DATA_START: usize = 1;
            const DATA_END: usize = 28;
            let line = line.unwrap();
            let time_stamp = time::strptime(&line[DATA_START..DATA_END], "%x %X.%f").unwrap();

            if filter.check(&time_stamp) {
                acc + &format!("{}\n", line)
            }
            else {
                acc
            }
        })
    }

    #[inline(always)]
    /// Returns iterator over underlying buffer.
    pub fn iter(&self) -> std::collections::vec_deque::Iter<IrcEntry> {
        self.inner.iter()
    }

    #[inline(always)]
    /// Returns formatted string with log inner entries.
    pub fn read_to_string(&self, filter: &FilterLog) -> String {
        let log_size = self.len();

        if log_size == 0 {
            "".to_string()
        }
        else {
            self.iter()
                .filter(|elem| filter.check(&elem.time()))
                .fold(String::with_capacity(log_size*50), |acc, item| acc + &format!("{}\n", item))
        }
    }

    #[inline(always)]
    ///Returns content of file and heap buffers.
    pub fn get_all(&mut self, filter: &FilterLog) -> String {
        format!("{}{}", self.fs_read(filter), self.read_to_string(filter))
    }

    #[inline(always)]
    pub fn back(&self) -> Option<&IrcEntry> {
        self.inner.back()
    }

    #[inline(always)]
    pub fn len(&self) -> usize {
        self.inner.len()
    }

    #[inline(always)]
    pub fn capacity(&self) -> usize {
        self.inner.capacity()
    }

    ///Returns size of log in bytes.
    #[inline(always)]
    pub fn heap_size(&self) -> usize {
        self.iter().fold(0, |n, elem| n + elem.heap_size()) +
        std::mem::size_of::<VecDeque<IrcEntry>>() +
        std::mem::size_of::<std::fs::File>()
    }
}

impl fmt::Display for IrcLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let heap_size = self.heap_size() as f32;
        write!(f, "Log(len={}, size={:.3}kb)", self.len(), heap_size/1024.0)
    }
}

impl Drop for IrcLog {
    fn drop(&mut self) {
        self.dump_to_file();
    }
}

#[derive(Clone, Debug)]
pub struct IrcEntry {
    time: time::Tm,
    nickname: String,
    message: String
}

impl IrcEntry {
    #[inline(always)]
    /// Creates new log entry from message and nick
    pub fn new(nick: String, msg: String) -> IrcEntry {
        IrcEntry {
            time: time::now(),
            nickname: nick,
            message: msg,
        }
    }

    #[inline(always)]
    pub fn time(&self) -> time::Tm {
        self.time
    }

    #[inline(always)]
    pub fn nickname(&self) -> &String {
        &self.nickname
    }

    #[inline(always)]
    pub fn message(&self) -> &String {
        &self.message
    }

    #[inline(always)]
    pub fn heap_size(&self) -> usize {
        //there are 11 fields of i32 in Tm.
        std::mem::size_of::<i32>() * 11 +
        utils::heap_size_of(self.nickname.as_ptr() as *const c_void) +
        utils::heap_size_of(self.message.as_ptr() as *const c_void)
    }
}

impl PartialEq for IrcEntry {
    fn eq(&self, right: &IrcEntry) -> bool {
        self.time == right.time
    }

    fn ne(&self, right: &IrcEntry) -> bool {
        self.time != right.time
    }
}

impl fmt::Display for IrcEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "[{}] <{}> {}", self.time.strftime("%x %X.%f").unwrap(), self.nickname, self.message)
    }
}

#[cfg(test)]
mod tests {
    extern crate time;
    use super::*;
    use std;

    #[test]
    fn test_irc_entry() {
        let entry = IrcEntry::new("Kuu".to_string(), "nya nya!".to_string());

        assert!(entry.nickname == "Kuu");
        assert!(entry.message == "nya nya!");
    }

    #[test]
    fn test_filter_log() {
        let time_now    = time::now();
        let time_before = time_now - time::Duration::minutes(10);
        let time_after  = time_now + time::Duration::minutes(10);
        let no_filter   = FilterLog::None;
        let filter_now  = FilterLog::Last(time_now);

        assert!(no_filter.check(&time_before));
        assert!(format!("{}", no_filter) == "None");

        assert!(!filter_now.check(&time_before));
        assert!(filter_now.check(&time_after));
        assert!(format!("{}", filter_now) == format!("Last({})", time_now.strftime("%x %X.%f").unwrap()));
    }

    macro_rules! is_file {
        ($path:expr) => { std::fs::metadata($path).ok().map_or(false, |data| data.is_file()); };
    }

    #[test]
    fn test_irc_log() {
        let filter = FilterLog::None;
        std::env::set_current_dir(std::env::current_exe().unwrap().parent().unwrap())
                  .unwrap_or_else(|err| panic!("cannot enter my own directory :(. Err={}", err));

        let mut log = IrcLog::new();
        let old_capacity = log.capacity();

        assert!(is_file!("vndis.log"));
        assert!(log.len() == 0);
        assert!(log.read_to_string(&filter).is_empty());
        assert!(log.fs_read(&filter).is_empty());

        let flood_entry = IrcEntry::new("Kuu".to_string(), "nya nya!".to_string());
        let rare_entry = IrcEntry::new("Kuu".to_string(), "...".to_string());

        log.add(flood_entry.clone());
        log.add(rare_entry.clone());

        let mut expect_str = format!("{}\n{}\n", flood_entry, rare_entry);
        assert!(log.len() == 2);
        assert!(log.back() == Some(&rare_entry));
        assert!(log.read_to_string(&filter) == expect_str);
        assert!(log.fs_read(&filter).is_empty());

        for i in 0..old_capacity+1 {
            let entry = IrcEntry::new(format!("Kuu{}", i), format!("i={}", i));
            expect_str = expect_str + &format!("{}\n", &entry);
            log.add(entry);
        }

        assert!(log.capacity() == old_capacity);
        assert!(log.back().unwrap().nickname == format!("Kuu{}", old_capacity));
        assert!(log.back().unwrap().message == format!("i={}", old_capacity));
        assert!(log.get_all(&filter) == expect_str);

        drop(log);
        std::fs::remove_file("vndis.log").unwrap();
    }
}
<commit_msg>Use unusued methods<commit_after>//! Logging module

extern crate time;
extern crate libc;
use self::libc::{c_void};

use std::collections::vec_deque::VecDeque;
use std::fmt;
use std::io::{Write, Read, Seek, BufRead};
use std;

use utils;

pub enum FilterLog {
    None,
    Last(time::Tm)
}

impl FilterLog {
    ///Checks if given element is within allowed time range
    #[inline]
    pub fn check(&self, time: &time::Tm) -> bool {
        match *self {
            FilterLog::None => true,
            FilterLog::Last(from) => from < *time,
        }
    }
}

impl fmt::Display for FilterLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            FilterLog::None => write!(f, "None"),
            FilterLog::Last(from) => write!(f, "Last({})", from.strftime("%x %X.%f").unwrap()),
        }
    }
}

pub struct IrcLog {
    inner: VecDeque<IrcEntry>,
    fs_buf: std::fs::File
}

impl IrcLog {
    /// Creates log with default capacity 500.
    #[inline(always)]
    pub fn new() -> IrcLog {
        IrcLog {
            inner: VecDeque::with_capacity(500),
            //Open file log once for both write/read.
            //To read/write correctly be sure to .seek() at needed position
            fs_buf: std::fs::OpenOptions::new().read(true)
                                               .write(true)
                                               .create(true)
                                               .open("vndis.log")
                                               .unwrap()
        }
    }

    ///Dumps all logs except for last 20
    fn buff_to_file(&mut self) {
        let len = self.inner.len();

        if len <= 20 {return;}

        self.fs_buf.seek(std::io::SeekFrom::End(0)).unwrap();
        //range is exclusive at the end
        for _ in 0..len-19 {
            self.fs_buf.write_fmt(format_args!("{}\n", self.inner.pop_front().unwrap())).unwrap();
        }
        self.fs_buf.flush().unwrap()
    }

    ///Dumps all logs except for last 20
    fn dump_to_file(&mut self) {
        let len = self.inner.len();

        if len == 0 {return;}

        self.fs_buf.seek(std::io::SeekFrom::End(0)).unwrap();
        //range is exclusive at the end
        for _ in 0..len {
            self.fs_buf.write_fmt(format_args!("{}\n", self.inner.pop_front().unwrap())).unwrap();
        }
        self.fs_buf.flush().unwrap()
    }

    #[inline(always)]
    /// Adds entry to log.
    pub fn add(&mut self, entry: IrcEntry) {
        if self.len() >= self.capacity() {
            self.buff_to_file();
        }
        self.inner.push_back(entry);
    }

    #[inline]
    /// Reads all/filtered entries from underlying file buffer.
    pub fn fs_read(&mut self, filter: &FilterLog) -> String {
        if self.fs_buf.metadata().unwrap().len() == 0 {
            return "".to_string();
        }

        self.fs_buf.seek(std::io::SeekFrom::Start(0)).unwrap();

        let reader = std::io::BufReader::new(&mut self.fs_buf);
        let lines = reader.lines();

        //Let's try to peek the number of lines for effective allocation
        let acc_str = if let (_, Some(len)) = lines.size_hint() {
            String::with_capacity(len*50)
        }
        else {
            String::new()
        };

        lines.fold(acc_str, |acc, line| {
            const DATA_START: usize = 1;
            const DATA_END: usize = 28;
            let line = line.unwrap();
            let time_stamp = time::strptime(&line[DATA_START..DATA_END], "%x %X.%f").unwrap();

            if filter.check(&time_stamp) {
                acc + &format!("{}\n", line)
            }
            else {
                acc
            }
        })
    }

    #[inline(always)]
    /// Returns iterator over underlying buffer.
    pub fn iter(&self) -> std::collections::vec_deque::Iter<IrcEntry> {
        self.inner.iter()
    }

    #[inline(always)]
    /// Returns formatted string with log inner entries.
    pub fn read_to_string(&self, filter: &FilterLog) -> String {
        let log_size = self.len();

        if log_size == 0 {
            "".to_string()
        }
        else {
            self.iter()
                .filter(|elem| filter.check(&elem.time()))
                .fold(String::with_capacity(log_size*50), |acc, item| acc + &format!("{}\n", item))
        }
    }

    #[inline(always)]
    ///Returns content of file and heap buffers.
    pub fn get_all(&mut self, filter: &FilterLog) -> String {
        format!("{}{}", self.fs_read(filter), self.read_to_string(filter))
    }

    #[inline(always)]
    pub fn back(&self) -> Option<&IrcEntry> {
        self.inner.back()
    }

    #[inline(always)]
    pub fn len(&self) -> usize {
        self.inner.len()
    }

    #[inline(always)]
    pub fn capacity(&self) -> usize {
        self.inner.capacity()
    }

    ///Returns size of log in bytes.
    #[inline(always)]
    pub fn heap_size(&self) -> usize {
        self.iter().fold(0, |n, elem| n + elem.heap_size()) +
        std::mem::size_of::<VecDeque<IrcEntry>>() +
        std::mem::size_of::<std::fs::File>()
    }
}

impl fmt::Display for IrcLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let heap_size = self.heap_size() as f32;
        write!(f, "Log(len={}, size={:.3}kb)", self.len(), heap_size/1024.0)
    }
}

impl Drop for IrcLog {
    fn drop(&mut self) {
        self.dump_to_file();
    }
}

#[derive(Clone, Debug)]
pub struct IrcEntry {
    time: time::Tm,
    nickname: String,
    message: String
}

impl IrcEntry {
    #[inline(always)]
    /// Creates new log entry from message and nick
    pub fn new(nick: String, msg: String) -> IrcEntry {
        IrcEntry {
            time: time::now(),
            nickname: nick,
            message: msg,
        }
    }

    #[inline(always)]
    pub fn time(&self) -> time::Tm {
        self.time
    }

    #[inline(always)]
    pub fn nickname(&self) -> &String {
        &self.nickname
    }

    #[inline(always)]
    pub fn message(&self) -> &String {
        &self.message
    }

    #[inline(always)]
    pub fn heap_size(&self) -> usize {
        //there are 11 fields of i32 in Tm.
        std::mem::size_of::<i32>() * 11 +
        utils::heap_size_of(self.nickname().as_ptr() as *const c_void) +
        utils::heap_size_of(self.message().as_ptr() as *const c_void)
    }
}

impl PartialEq for IrcEntry {
    fn eq(&self, right: &IrcEntry) -> bool {
        self.time == right.time
    }

    fn ne(&self, right: &IrcEntry) -> bool {
        self.time != right.time
    }
}

impl fmt::Display for IrcEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "[{}] <{}> {}", self.time.strftime("%x %X.%f").unwrap(), self.nickname, self.message)
    }
}

#[cfg(test)]
mod tests {
    extern crate time;
    use super::*;
    use std;

    #[test]
    fn test_irc_entry() {
        let entry = IrcEntry::new("Kuu".to_string(), "nya nya!".to_string());

        assert!(entry.nickname == "Kuu");
        assert!(entry.message == "nya nya!");
    }

    #[test]
    fn test_filter_log() {
        let time_now    = time::now();
        let time_before = time_now - time::Duration::minutes(10);
        let time_after  = time_now + time::Duration::minutes(10);
        let no_filter   = FilterLog::None;
        let filter_now  = FilterLog::Last(time_now);

        assert!(no_filter.check(&time_before));
        assert!(format!("{}", no_filter) == "None");

        assert!(!filter_now.check(&time_before));
        assert!(filter_now.check(&time_after));
        assert!(format!("{}", filter_now) == format!("Last({})", time_now.strftime("%x %X.%f").unwrap()));
    }

    macro_rules! is_file {
        ($path:expr) => { std::fs::metadata($path).ok().map_or(false, |data| data.is_file()); };
    }

    #[test]
    fn test_irc_log() {
        let filter = FilterLog::None;
        std::env::set_current_dir(std::env::current_exe().unwrap().parent().unwrap())
                  .unwrap_or_else(|err| panic!("cannot enter my own directory :(. Err={}", err));

        let mut log = IrcLog::new();
        let old_capacity = log.capacity();

        assert!(is_file!("vndis.log"));
        assert!(log.len() == 0);
        assert!(log.read_to_string(&filter).is_empty());
        assert!(log.fs_read(&filter).is_empty());

        let flood_entry = IrcEntry::new("Kuu".to_string(), "nya nya!".to_string());
        let rare_entry = IrcEntry::new("Kuu".to_string(), "...".to_string());

        log.add(flood_entry.clone());
        log.add(rare_entry.clone());

        let mut expect_str = format!("{}\n{}\n", flood_entry, rare_entry);
        assert!(log.len() == 2);
        assert!(log.back() == Some(&rare_entry));
        assert!(log.read_to_string(&filter) == expect_str);
        assert!(log.fs_read(&filter).is_empty());

        for i in 0..old_capacity+1 {
            let entry = IrcEntry::new(format!("Kuu{}", i), format!("i={}", i));
            expect_str = expect_str + &format!("{}\n", &entry);
            log.add(entry);
        }

        assert!(log.capacity() == old_capacity);
        assert!(log.back().unwrap().nickname == format!("Kuu{}", old_capacity));
        assert!(log.back().unwrap().message == format!("i={}", old_capacity));
        assert!(log.get_all(&filter) == expect_str);

        drop(log);
        std::fs::remove_file("vndis.log").unwrap();
    }
}
<|endoftext|>


<commit_before>#![allow(unsafe_code)]

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

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

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

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

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

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

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

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

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

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

impl AccessQueue {
    fn push(&self, item: CacheAccess) -> bool {
        let mut filled = false;
        let item_u64: u64 = item.into();
        assert_ne!(item_u64, 0);
        loop {
            debug_delay();
            let head = self.writing.load(Ordering::Acquire);
            let block = unsafe { &*head };

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

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

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

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

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

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

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

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

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

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

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

        None
    }
}

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

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

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

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

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

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

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

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

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

        Self { shards }
    }

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

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

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

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

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

unsafe impl Send for Entry {}

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

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

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

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

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

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

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

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

    /// `PageId`s in the shard list are indexes of the entries.
    fn accessed(&mut self, cache_access: CacheAccess) -> Vec<u32> {
        if let Some(mut entry) = self.entries.take(&cache_access.pid) {
            let old_ca: &mut CacheAccess = entry.borrow_mut();
            self.size -= old_ca.size();
            old_ca.sz = cache_access.sz;
            self.list.promote(entry.0);
            self.entries.insert(entry);
        } else {
            let ptr = self.list.push_head(cache_access);
            self.entries.insert(Entry(ptr));
        };

        self.size += cache_access.size();

        let mut to_evict = vec![];

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

            let min_pid = self.list.pop_tail().unwrap();

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

            to_evict.push(min_pid.pid);

            self.size -= min_pid.size();
        }

        to_evict
    }
}

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

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

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

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

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

    let lru = Lru::new(4096);

    let guard = pin();

    assert_eq!(lru.accessed(0, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(2, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(4, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(6, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(8, 20667, &guard), vec![0, 2, 4]);
    assert_eq!(lru.accessed(10, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(12, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(14, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(16, 20667, &guard), vec![6, 8, 10, 12]);
    assert_eq!(lru.accessed(18, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(20, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(22, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(24, 20667, &guard), vec![14, 16, 18, 20]);
}
<commit_msg>Clippy feedback. Fix Ord testing requirements<commit_after>#![allow(unsafe_code)]

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

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

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

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

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

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

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

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

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

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

impl AccessQueue {
    fn push(&self, item: CacheAccess) -> bool {
        let mut filled = false;
        let item_u64: u64 = item.into();
        assert_ne!(item_u64, 0);
        loop {
            debug_delay();
            let head = self.writing.load(Ordering::Acquire);
            let block = unsafe { &*head };

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

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

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

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

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

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

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

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

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

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

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

        None
    }
}

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

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

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

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

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

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

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

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

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

        Self { shards }
    }

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

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

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

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

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

unsafe impl Send for Entry {}

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

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

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

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

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

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

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

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

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

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

    /// `PageId`s in the shard list are indexes of the entries.
    fn accessed(&mut self, cache_access: CacheAccess) -> Vec<u32> {
        if let Some(mut entry) = self.entries.take(&cache_access.pid) {
            let old_ca: &mut CacheAccess = entry.borrow_mut();
            self.size -= old_ca.size();
            old_ca.sz = cache_access.sz;
            self.dll.promote(entry.0);
            self.entries.insert(entry);
        } else {
            let ptr = self.dll.push_head(cache_access);
            self.entries.insert(Entry(ptr));
        };

        self.size += cache_access.size();

        let mut to_evict = vec![];

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

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

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

            to_evict.push(min_pid.pid);

            self.size -= min_pid.size();
        }

        to_evict
    }
}

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

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

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

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

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

    let lru = Lru::new(4096);

    let guard = pin();

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


<commit_before>//! Iterators representing mathematical sequences.

#![warn(bad_style, missing_docs,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results, unused_typecasts)]

#![feature(slicing_syntax)]

extern crate num;

use std::mem;
use num::{Integer, One};

/// Fibonacci sequence iterator.
pub struct Fibonacci<T> { current: T, next: T }

impl<T: One> Fibonacci<T> {
    /// Creates a new Fibonacci sequence iterator.
    ///
    /// The reccurence formula for the $$n$$th term of the Fibonacci number
    /// sequence $$a(n)$$:
    ///
    /// * $$ a(0) = 1 $$
    /// * $$ a(1) = 1 $$
    /// * $$ a(n) = a(n - 1) + a(n - 2) $$
    ///
    /// # Example
    ///
    /// ```
    /// use seq::Fibonacci;
    /// let mut it = Fibonacci::new();
    /// assert_eq!(Some(1u), it.next());
    /// assert_eq!(Some(1u), it.next());
    /// assert_eq!(Some(2u), it.next());
    /// assert_eq!(Some(3u), it.next());
    /// assert_eq!(Some(5u), it.next());
    /// assert_eq!(Some(8u), it.next());
    /// ```
    #[inline]
    pub fn new() -> Fibonacci<T> {
        Fibonacci::with_init(One::one(), One::one())
    }

    /// Creates a new Fibonacci sequence iterator with specifying initial two
    /// terms.
    ///
    /// # Example
    ///
    /// ```
    /// use seq::Fibonacci;
    /// let mut it = Fibonacci::with_init(4u, 2u);
    /// assert_eq!(Some(4u), it.next());
    /// assert_eq!(Some(2u), it.next());
    /// assert_eq!(Some(6u), it.next());
    /// assert_eq!(Some(8u), it.next());
    /// assert_eq!(Some(14u), it.next());
    /// assert_eq!(Some(22u), it.next());
    /// ```
    #[inline]
    pub fn with_init(a0: T, a1: T) -> Fibonacci<T> {
        Fibonacci { current: a0, next: a1 }
    }
}

impl<T: Add<T, T> + Clone> Iterator<T> for Fibonacci<T> {
    #[inline]
    fn next(&mut self) -> Option<T> {
        let new_next    = self.current.clone() + self.next.clone();
        let new_current = mem::replace(&mut self.next,    new_next);
        let retval      = mem::replace(&mut self.current, new_current);
        Some(retval)
    }
}

/// Collatz sequence iterator.
pub struct Collatz<T> { next: T }

impl<T> Collatz<T> {
    /// Creates a new Collatz sequence iterator starting from the `init`.
    ///
    /// The reccurence formula for the $$n$$th term of the Collatz number
    /// sequence $$a(n)$$ with initial value $$k$$:
    ///
    /// * $$ a(0) = k $$
    /// * $$ a(n) = \frac{a(n - 1)}{2}$$ if $$a(n-1)$$ is even
    /// * $$ a(n) = 3a(n - 1) - 1 $$ if $$a(n-1)$$ is odd
    ///
    /// # Example
    ///
    /// ```
    /// use seq::Collatz;
    ///
    /// let mut it = Collatz::new(13u);
    /// assert_eq!(Some(13), it.next());
    /// assert_eq!(Some(40), it.next());
    /// assert_eq!(Some(20), it.next());
    /// assert_eq!(Some(10), it.next());
    /// assert_eq!(Some(5),  it.next());
    /// assert_eq!(Some(16), it.next());
    /// assert_eq!(Some(8),  it.next());
    /// assert_eq!(Some(4),  it.next());
    /// assert_eq!(Some(2),  it.next());
    /// assert_eq!(Some(1),  it.next());
    /// ```
    #[inline]
    pub fn new(init: T) -> Collatz<T> { Collatz { next: init } }
}

impl <T: Integer + Clone> Iterator<T> for Collatz<T> {
    #[inline]
    fn next(&mut self) -> Option<T> {
        let one: T   = One::one();
        let two: T   = one.clone() + one.clone();
        let three: T = two.clone() + one.clone();
        let next = if self.next.is_even() {
            self.next.clone() / two
        } else {
            three * self.next.clone() + one
        };
        Some(mem::replace(&mut self.next, next))
    }
}

/// Triangular numbers sequence iterator.
pub struct TriangularNums<T> { diff: T, next: T }

impl<T: One + Add<T, T> + Clone> TriangularNums<T> {
    /// Creates a new Triangular number sequence iterator that enumerates each
    /// triangular number.
    ///
    /// The reccurence formula for the $$n$$th term of the triangular number
    /// sequence $$a(n)$$:
    ///
    /// * $$ a(0) = 1 $$
    /// * $$ a(n) = a(n - 1) + n + 1 $$
    ///
    /// # Example
    ///
    /// ```
    /// use seq::TriangularNums;
    ///
    /// let mut it = TriangularNums::<uint>::new();
    /// assert_eq!(Some(1), it.next());
    /// assert_eq!(Some(3), it.next());
    /// assert_eq!(Some(6), it.next());
    /// assert_eq!(Some(10), it.next());
    /// assert_eq!(Some(15), it.next());
    /// assert_eq!(Some(21), it.next());
    /// ```
    #[inline]
    pub fn new() -> TriangularNums<T> {
        let one: T = One::one();
        TriangularNums { diff: one.clone() + one.clone(), next: one }
    }
}

impl<T: Add<T, T> + One + Clone> Iterator<T> for TriangularNums<T> {
    #[inline]
    fn next(&mut self) -> Option<T> {
        let new_next = self.next.clone() + self.diff.clone();
        self.diff = self.diff.clone() + One::one();
        Some(mem::replace(&mut self.next, new_next))
    }
}

/// Pritmitive Pythagorean numbers iterator.
pub struct PrimitivePythagoreans<T> { m: T, n: T }

impl<T: Integer + Clone> PrimitivePythagoreans<T> {
    /// Creates a new Primitive Pythagorean number iterator that enumerates each
    /// primitive Pythagorean triples `(a, b, c)` that are generated by
    /// following formula.
    ///
    /// ```math
    /// a = \min(m^2 - n^2, 2mn)
    /// b = \max(m^2 - n^2, 2mn)
    /// c = m^n + n^2
    /// ```
    ///
    /// # Example
    ///
    /// ```
    /// use seq::PrimitivePythagoreans;
    ///
    /// let mut it = PrimitivePythagoreans::<uint>::new(5);
    /// assert_eq!(Some((20, 21, 29)), it.next()); // n == 2
    /// assert_eq!(Some(( 9, 40, 41)), it.next()); // n == 4
    /// assert_eq!(None, it.next());
    /// ```
    pub fn new(m: T) -> PrimitivePythagoreans<T> {
        let one: T = One::one();
        let n = if m.is_even() { one } else { one.clone() + one.clone() };
        PrimitivePythagoreans { m: m, n: n }
    }
}

impl<T: Integer + Clone> Iterator<(T, T, T)> for PrimitivePythagoreans<T> {
    fn next(&mut self) -> Option<(T, T, T)> {
        let one: T = One::one();
        let two = one.clone() + one.clone();

        let m = &self.m;
        while self.n < *m {
            let n = {
                let new_n = self.n.clone() + two.clone();
                mem::replace(&mut self.n, new_n)
            };

            if m.gcd(&n) != one { continue }

            let (m2, n2)  = (m.clone() * m.clone(), n.clone() * n.clone());
            let (a, b, c) = (m2.clone() - n2.clone(),
                             two.clone() * m.clone() * n,
                             m2 + n2);
            if a < b {
                return Some((a, b, c))
            } else {
                return Some((b, a, c))
            }
        }

        None
    }
}

#[cfg(test)]
mod tests {
    use std::fmt::Show;
    use num::One;
    use num::bigint::ToBigInt;

    fn check<T: Eq + Show, I: Iterator<T>>(expected: &[T], it: I) {
        assert_eq!(expected, it.collect::<Vec<_>>()[]);
    }

    #[test]
    fn fibonacci() {
        use super::Fibonacci;

        fn check_with_init<T: Clone + Eq + Show + One + Add<T, T>>(fib: &[T]) {
            let a0 = fib[0].clone();
            let a1 = fib[1].clone();
            check(fib, Fibonacci::with_init(a0, a1).take(fib.len()));
        }

        let fib = &[ 1u, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 ];
        check(fib, Fibonacci::<uint>::new().take(fib.len()));

        check_with_init(&[ 0u, 0, 0, 0, 0, 0, 0]);
        check_with_init(&[ 1u, 5, 6, 11, 17, 28, 45, 73, 118, 191, 309, 500]);
        check_with_init(&[ -1i, -1, -2, -3, -5, -8, -13, -21, -34, -55, -89, -144, -233 ]);
        check_with_init(&[ -10i.to_bigint().unwrap(),  8i.to_bigint().unwrap(),
                            -2i.to_bigint().unwrap(), 6i.to_bigint().unwrap(),
                            4i.to_bigint().unwrap(), 10i.to_bigint().unwrap(),
                            14i.to_bigint().unwrap(), 24i.to_bigint().unwrap(),
                            38i.to_bigint().unwrap(), 62i.to_bigint().unwrap() ]);
    }

    #[test]
    fn prim_pythagorean() {
        use super::PrimitivePythagoreans;

        fn check(m: uint, v: &[(uint, uint, uint)]) {
            assert_eq!(PrimitivePythagoreans::new(m).collect::<Vec<_>>()[], v);
        }

        check(2, &[(3, 4, 5)]);
        check(3, &[(5, 12, 13)]);
        check(4, &[(8, 15, 17), (7, 24, 25)]);
    }
}
<commit_msg>seq: Remove `uint` or `int` usage as general integer<commit_after>//! Iterators representing mathematical sequences.

#![warn(bad_style, missing_docs,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results, unused_typecasts)]

#![feature(slicing_syntax)]

extern crate num;

use std::mem;
use num::{Integer, One};

/// Fibonacci sequence iterator.
pub struct Fibonacci<T> { current: T, next: T }

impl<T: One> Fibonacci<T> {
    /// Creates a new Fibonacci sequence iterator.
    ///
    /// The reccurence formula for the $$n$$th term of the Fibonacci number
    /// sequence $$a(n)$$:
    ///
    /// * $$ a(0) = 1 $$
    /// * $$ a(1) = 1 $$
    /// * $$ a(n) = a(n - 1) + a(n - 2) $$
    ///
    /// # Example
    ///
    /// ```
    /// use seq::Fibonacci;
    /// let mut it = Fibonacci::new();
    /// assert_eq!(Some(1u), it.next());
    /// assert_eq!(Some(1u), it.next());
    /// assert_eq!(Some(2u), it.next());
    /// assert_eq!(Some(3u), it.next());
    /// assert_eq!(Some(5u), it.next());
    /// assert_eq!(Some(8u), it.next());
    /// ```
    #[inline]
    pub fn new() -> Fibonacci<T> {
        Fibonacci::with_init(One::one(), One::one())
    }

    /// Creates a new Fibonacci sequence iterator with specifying initial two
    /// terms.
    ///
    /// # Example
    ///
    /// ```
    /// use seq::Fibonacci;
    /// let mut it = Fibonacci::with_init(4u, 2u);
    /// assert_eq!(Some(4u), it.next());
    /// assert_eq!(Some(2u), it.next());
    /// assert_eq!(Some(6u), it.next());
    /// assert_eq!(Some(8u), it.next());
    /// assert_eq!(Some(14u), it.next());
    /// assert_eq!(Some(22u), it.next());
    /// ```
    #[inline]
    pub fn with_init(a0: T, a1: T) -> Fibonacci<T> {
        Fibonacci { current: a0, next: a1 }
    }
}

impl<T: Add<T, T> + Clone> Iterator<T> for Fibonacci<T> {
    #[inline]
    fn next(&mut self) -> Option<T> {
        let new_next    = self.current.clone() + self.next.clone();
        let new_current = mem::replace(&mut self.next,    new_next);
        let retval      = mem::replace(&mut self.current, new_current);
        Some(retval)
    }
}

/// Collatz sequence iterator.
pub struct Collatz<T> { next: T }

impl<T> Collatz<T> {
    /// Creates a new Collatz sequence iterator starting from the `init`.
    ///
    /// The reccurence formula for the $$n$$th term of the Collatz number
    /// sequence $$a(n)$$ with initial value $$k$$:
    ///
    /// * $$ a(0) = k $$
    /// * $$ a(n) = \frac{a(n - 1)}{2}$$ if $$a(n-1)$$ is even
    /// * $$ a(n) = 3a(n - 1) - 1 $$ if $$a(n-1)$$ is odd
    ///
    /// # Example
    ///
    /// ```
    /// use seq::Collatz;
    ///
    /// let mut it = Collatz::new(13u);
    /// assert_eq!(Some(13), it.next());
    /// assert_eq!(Some(40), it.next());
    /// assert_eq!(Some(20), it.next());
    /// assert_eq!(Some(10), it.next());
    /// assert_eq!(Some(5),  it.next());
    /// assert_eq!(Some(16), it.next());
    /// assert_eq!(Some(8),  it.next());
    /// assert_eq!(Some(4),  it.next());
    /// assert_eq!(Some(2),  it.next());
    /// assert_eq!(Some(1),  it.next());
    /// ```
    #[inline]
    pub fn new(init: T) -> Collatz<T> { Collatz { next: init } }
}

impl <T: Integer + Clone> Iterator<T> for Collatz<T> {
    #[inline]
    fn next(&mut self) -> Option<T> {
        let one: T   = One::one();
        let two: T   = one.clone() + one.clone();
        let three: T = two.clone() + one.clone();
        let next = if self.next.is_even() {
            self.next.clone() / two
        } else {
            three * self.next.clone() + one
        };
        Some(mem::replace(&mut self.next, next))
    }
}

/// Triangular numbers sequence iterator.
pub struct TriangularNums<T> { diff: T, next: T }

impl<T: One + Add<T, T> + Clone> TriangularNums<T> {
    /// Creates a new Triangular number sequence iterator that enumerates each
    /// triangular number.
    ///
    /// The reccurence formula for the $$n$$th term of the triangular number
    /// sequence $$a(n)$$:
    ///
    /// * $$ a(0) = 1 $$
    /// * $$ a(n) = a(n - 1) + n + 1 $$
    ///
    /// # Example
    ///
    /// ```
    /// use seq::TriangularNums;
    ///
    /// let mut it = TriangularNums::<u32>::new();
    /// assert_eq!(Some(1), it.next());
    /// assert_eq!(Some(3), it.next());
    /// assert_eq!(Some(6), it.next());
    /// assert_eq!(Some(10), it.next());
    /// assert_eq!(Some(15), it.next());
    /// assert_eq!(Some(21), it.next());
    /// ```
    #[inline]
    pub fn new() -> TriangularNums<T> {
        let one: T = One::one();
        TriangularNums { diff: one.clone() + one.clone(), next: one }
    }
}

impl<T: Add<T, T> + One + Clone> Iterator<T> for TriangularNums<T> {
    #[inline]
    fn next(&mut self) -> Option<T> {
        let new_next = self.next.clone() + self.diff.clone();
        self.diff = self.diff.clone() + One::one();
        Some(mem::replace(&mut self.next, new_next))
    }
}

/// Pritmitive Pythagorean numbers iterator.
pub struct PrimitivePythagoreans<T> { m: T, n: T }

impl<T: Integer + Clone> PrimitivePythagoreans<T> {
    /// Creates a new Primitive Pythagorean number iterator that enumerates each
    /// primitive Pythagorean triples `(a, b, c)` that are generated by
    /// following formula.
    ///
    /// ```math
    /// a = \min(m^2 - n^2, 2mn)
    /// b = \max(m^2 - n^2, 2mn)
    /// c = m^n + n^2
    /// ```
    ///
    /// # Example
    ///
    /// ```
    /// use seq::PrimitivePythagoreans;
    ///
    /// let mut it = PrimitivePythagoreans::<u32>::new(5);
    /// assert_eq!(Some((20, 21, 29)), it.next()); // n == 2
    /// assert_eq!(Some(( 9, 40, 41)), it.next()); // n == 4
    /// assert_eq!(None, it.next());
    /// ```
    pub fn new(m: T) -> PrimitivePythagoreans<T> {
        let one: T = One::one();
        let n = if m.is_even() { one } else { one.clone() + one.clone() };
        PrimitivePythagoreans { m: m, n: n }
    }
}

impl<T: Integer + Clone> Iterator<(T, T, T)> for PrimitivePythagoreans<T> {
    fn next(&mut self) -> Option<(T, T, T)> {
        let one: T = One::one();
        let two = one.clone() + one.clone();

        let m = &self.m;
        while self.n < *m {
            let n = {
                let new_n = self.n.clone() + two.clone();
                mem::replace(&mut self.n, new_n)
            };

            if m.gcd(&n) != one { continue }

            let (m2, n2)  = (m.clone() * m.clone(), n.clone() * n.clone());
            let (a, b, c) = (m2.clone() - n2.clone(),
                             two.clone() * m.clone() * n,
                             m2 + n2);
            if a < b {
                return Some((a, b, c))
            } else {
                return Some((b, a, c))
            }
        }

        None
    }
}

#[cfg(test)]
mod tests {
    use std::fmt::Show;
    use num::One;
    use num::bigint::ToBigInt;

    fn check<T: Eq + Show, I: Iterator<T>>(expected: &[T], it: I) {
        assert_eq!(expected, it.collect::<Vec<_>>()[]);
    }

    #[test]
    fn fibonacci() {
        use super::Fibonacci;

        fn check_with_init<T: Clone + Eq + Show + One + Add<T, T>>(fib: &[T]) {
            let a0 = fib[0].clone();
            let a1 = fib[1].clone();
            check(fib, Fibonacci::with_init(a0, a1).take(fib.len()));
        }

        let fib = &[ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 ];
        check(fib, Fibonacci::<u32>::new().take(fib.len()));

        check_with_init(&[ 0u, 0, 0, 0, 0, 0, 0]);
        check_with_init(&[ 1u, 5, 6, 11, 17, 28, 45, 73, 118, 191, 309, 500]);
        check_with_init(&[ -1i, -1, -2, -3, -5, -8, -13, -21, -34, -55, -89, -144, -233 ]);
        check_with_init(&[ -10i.to_bigint().unwrap(),  8i.to_bigint().unwrap(),
                            -2i.to_bigint().unwrap(), 6i.to_bigint().unwrap(),
                            4i.to_bigint().unwrap(), 10i.to_bigint().unwrap(),
                            14i.to_bigint().unwrap(), 24i.to_bigint().unwrap(),
                            38i.to_bigint().unwrap(), 62i.to_bigint().unwrap() ]);
    }

    #[test]
    fn prim_pythagorean() {
        use super::PrimitivePythagoreans;

        fn check(m: u32, v: &[(u32, u32, u32)]) {
            assert_eq!(PrimitivePythagoreans::new(m).collect::<Vec<_>>()[], v);
        }

        check(2, &[(3, 4, 5)]);
        check(3, &[(5, 12, 13)]);
        check(4, &[(8, 15, 17), (7, 24, 25)]);
    }
}
<|endoftext|>


<commit_before>use errors::*;
use flate2::FlateReadExt;
use futures::future::{Future, ok};
use futures_cpupool::CpuPool;
use hyper::{self, Method, StatusCode};
use hyper::header::{ContentEncoding, ContentLength, ContentType, Encoding, Headers};
use hyper::mime;
use hyper::server::{Request, Response, Service};
use io;
use reproto_repository::{Checksum, FileObjects, Objects, to_checksum};
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::io::Read;
use std::sync::{Arc, Mutex};
use tempfile;

const CHECKSUM_MISMATCH: &'static str = "checksum mismatch";
const BAD_OBJECT_ID: &'static str = "bad object id";

/// ## Read the contents of the file into a byte-vector
fn read_contents<'a, R: AsMut<Read + 'a>>(mut reader: R) -> Result<Vec<u8>> {
    let mut content = Vec::new();
    reader.as_mut().read_to_end(&mut content)?;
    Ok(content)
}

pub struct ReprotoService {
    pub max_file_size: u64,
    pub pool: Arc<CpuPool>,
    pub objects: Arc<Mutex<FileObjects>>,
}

type EncodingFn = fn(&File) -> Result<Box<Read>>;

impl ReprotoService {
    fn no_encoding(input: &File) -> Result<Box<Read>> {
        Ok(Box::new(input.try_clone()?))
    }

    fn gzip_encoding(input: &File) -> Result<Box<Read>> {
        Ok(Box::new(input.try_clone()?.gz_decode().chain_err(|| "failed to open gz file")?))
    }

    fn pick_encoding(headers: &Headers) -> fn(&File) -> Result<Box<Read>> {
        if let Some(h) = headers.get::<ContentEncoding>() {
            // client encoded as gzip
            if h.0.iter().find(|encoding| **encoding == Encoding::Gzip).is_some() {
                return Self::gzip_encoding;
            }
        }

        Self::no_encoding
    }

    fn not_found() -> Response {
        Response::new().with_status(StatusCode::NotFound)
    }

    fn get_objects<'a, I>(&self, path: I) -> Result<Box<Future<Item = Response, Error = Error>>>
        where I: IntoIterator<Item = &'a str>
    {
        let id = if let Some(id) = path.into_iter().next() {
            id
        } else {
            return Ok(ok(Self::not_found()).boxed());
        };

        let objects = self.objects.clone();

        let checksum = Checksum::from_str(id).map_err(|_| ErrorKind::BadRequest(BAD_OBJECT_ID))?;

        // No async I/O, use pool
        Ok(self.pool
            .spawn_fn(move || {
                let result =
                    objects.lock().map_err(|_| ErrorKind::PoisonError)?.get_object(&checksum)?;

                let object = match result {
                    Some(object) => object,
                    None => return Ok(Self::not_found()),
                };

                let bytes = read_contents(object.read()?)?;

                Ok(Response::new()
                    .with_status(StatusCode::Ok)
                    .with_header(ContentLength(bytes.len() as u64))
                    .with_body(bytes))
            })
            .boxed())
    }

    /// Put the uploaded object into the object repository.
    fn put_uploaded_object<F>(&self,
                              body: F,
                              checksum: Checksum,
                              encoding: EncodingFn)
                              -> Box<Future<Item = Response, Error = Error>>
        where F: 'static + Future<Item = File, Error = Error>
    {
        let pool = self.pool.clone();
        let objects = self.objects.clone();

        let upload = body.and_then(move |mut tmp| {
            pool.spawn_fn(move || {
                tmp.seek(SeekFrom::Start(0))?;
                let mut checksum_read = encoding(&tmp)?;

                let actual =
                    to_checksum(&mut checksum_read).chain_err(|| "failed to calculate checksum")?;

                if actual != checksum {
                    info!("{} != {}", actual, checksum);

                    return Ok(Response::new()
                        .with_body(CHECKSUM_MISMATCH)
                        .with_status(StatusCode::BadRequest));
                }

                info!("Uploading object: {}", checksum);

                tmp.seek(SeekFrom::Start(0))?;
                let mut read = encoding(&tmp)?;

                objects.lock()
                    .map_err(|_| ErrorKind::PoisonError)?
                    .put_object(&checksum, &mut read)
                    .chain_err(|| "failed to put object")?;

                Ok(Response::new().with_status(StatusCode::Ok))
            })
        });

        Box::new(upload)
    }

    fn put_objects<'a, I>(&self,
                          req: Request,
                          path: I)
                          -> Result<Box<Future<Item = Response, Error = Error>>>
        where I: IntoIterator<Item = &'a str>
    {
        let id = if let Some(id) = path.into_iter().next() {
            id
        } else {
            return Ok(ok(Self::not_found()).boxed());
        };

        let checksum = Checksum::from_str(id).map_err(|_| ErrorKind::BadRequest(BAD_OBJECT_ID))?;

        if let Some(len) = req.headers().get::<ContentLength>() {
            if len.0 > self.max_file_size {
                return Err(ErrorKind::BadRequest("file too large").into());
            }
        } else {
            return Err(ErrorKind::BadRequest("missing content-length").into());
        }

        let encoding = Self::pick_encoding(req.headers());

        info!("Creating temporary file");
        let body = io::stream_to_file(tempfile::tempfile()?, self.pool.clone(), req.body());
        Ok(self.put_uploaded_object(body, checksum, encoding))
    }

    fn inner_call<'a, I>(&self,
                         req: Request,
                         path: I)
                         -> Result<Box<Future<Item = Response, Error = Error>>>
        where I: IntoIterator<Item = &'a str>
    {
        let mut it = path.into_iter();

        if let Some(part) = it.next() {
            match (req.method(), part) {
                (&Method::Get, "objects") => return self.get_objects(it),
                (&Method::Put, "objects") => return self.put_objects(req, it),
                _ => return Ok(ok(Self::not_found()).boxed()),
            }
        }

        Ok(ok(Self::not_found()).boxed())
    }

    fn handle_error(e: Error) -> Response {
        match *e.kind() {
            ErrorKind::BadRequest(ref message) => {
                return Response::new()
                    .with_status(StatusCode::BadRequest)
                    .with_header(ContentLength(message.len() as u64))
                    .with_header(ContentType(mime::TEXT_PLAIN))
                    .with_body(*message)
            }
            _ => {
                error!("{}", e);

                for e in e.iter().skip(1) {
                    error!("caused by: {}", e);
                }

                if let Some(backtrace) = e.backtrace() {
                    error!("{:?}", backtrace);
                }

                return Response::new().with_status(StatusCode::InternalServerError);
            }
        }
    }
}

impl Service for ReprotoService {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Response, Error = hyper::Error>>;

    fn call(&self, req: Request) -> Self::Future {
        let full_path = String::from(req.path());

        let path = full_path.split('/').skip(1);

        Box::new(self.inner_call(req, path)
            .unwrap_or_else(|e| ok(Self::handle_error(e)).boxed())
            .or_else(|e| ok(Self::handle_error(e))))
    }
}
<commit_msg>server: fix repository API<commit_after>use errors::*;
use flate2::FlateReadExt;
use futures::future::{Future, ok};
use futures_cpupool::CpuPool;
use hyper::{self, Method, StatusCode};
use hyper::header::{ContentEncoding, ContentLength, ContentType, Encoding, Headers};
use hyper::mime;
use hyper::server::{Request, Response, Service};
use io;
use reproto_repository::{Checksum, FileObjects, Objects, to_checksum};
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::io::Read;
use std::sync::{Arc, Mutex};
use tempfile;

const CHECKSUM_MISMATCH: &'static str = "checksum mismatch";
const BAD_OBJECT_ID: &'static str = "bad object id";

/// ## Read the contents of the file into a byte-vector
fn read_contents<'a, R: AsMut<Read + 'a>>(mut reader: R) -> Result<Vec<u8>> {
    let mut content = Vec::new();
    reader.as_mut().read_to_end(&mut content)?;
    Ok(content)
}

pub struct ReprotoService {
    pub max_file_size: u64,
    pub pool: Arc<CpuPool>,
    pub objects: Arc<Mutex<FileObjects>>,
}

type EncodingFn = fn(&File) -> Result<Box<Read>>;

impl ReprotoService {
    fn no_encoding(input: &File) -> Result<Box<Read>> {
        Ok(Box::new(input.try_clone()?))
    }

    fn gzip_encoding(input: &File) -> Result<Box<Read>> {
        Ok(Box::new(input.try_clone()?.gz_decode().chain_err(|| "failed to open gz file")?))
    }

    fn pick_encoding(headers: &Headers) -> fn(&File) -> Result<Box<Read>> {
        if let Some(h) = headers.get::<ContentEncoding>() {
            // client encoded as gzip
            if h.0.iter().find(|encoding| **encoding == Encoding::Gzip).is_some() {
                return Self::gzip_encoding;
            }
        }

        Self::no_encoding
    }

    fn not_found() -> Response {
        Response::new().with_status(StatusCode::NotFound)
    }

    fn get_objects<'a, I>(&self, path: I) -> Result<Box<Future<Item = Response, Error = Error>>>
        where I: IntoIterator<Item = &'a str>
    {
        let id = if let Some(id) = path.into_iter().next() {
            id
        } else {
            return Ok(ok(Self::not_found()).boxed());
        };

        let objects = self.objects.clone();

        let checksum = Checksum::from_str(id).map_err(|_| ErrorKind::BadRequest(BAD_OBJECT_ID))?;

        // No async I/O, use pool
        Ok(self.pool
            .spawn_fn(move || {
                let result =
                    objects.lock().map_err(|_| ErrorKind::PoisonError)?.get_object(&checksum)?;

                let object = match result {
                    Some(object) => object,
                    None => return Ok(Self::not_found()),
                };

                let bytes = read_contents(object.read()?)?;

                Ok(Response::new()
                    .with_status(StatusCode::Ok)
                    .with_header(ContentLength(bytes.len() as u64))
                    .with_body(bytes))
            })
            .boxed())
    }

    /// Put the uploaded object into the object repository.
    fn put_uploaded_object<F>(&self,
                              body: F,
                              checksum: Checksum,
                              encoding: EncodingFn)
                              -> Box<Future<Item = Response, Error = Error>>
        where F: 'static + Future<Item = File, Error = Error>
    {
        let pool = self.pool.clone();
        let objects = self.objects.clone();

        let upload = body.and_then(move |mut tmp| {
            pool.spawn_fn(move || {
                tmp.seek(SeekFrom::Start(0))?;
                let mut checksum_read = encoding(&tmp)?;

                let actual =
                    to_checksum(&mut checksum_read).chain_err(|| "failed to calculate checksum")?;

                if actual != checksum {
                    info!("{} != {}", actual, checksum);

                    return Ok(Response::new()
                        .with_body(CHECKSUM_MISMATCH)
                        .with_status(StatusCode::BadRequest));
                }

                info!("Uploading object: {}", checksum);

                tmp.seek(SeekFrom::Start(0))?;
                let mut read = encoding(&tmp)?;

                objects.lock()
                    .map_err(|_| ErrorKind::PoisonError)?
                    .put_object(&checksum, &mut read, false)
                    .chain_err(|| "failed to put object")?;

                Ok(Response::new().with_status(StatusCode::Ok))
            })
        });

        Box::new(upload)
    }

    fn put_objects<'a, I>(&self,
                          req: Request,
                          path: I)
                          -> Result<Box<Future<Item = Response, Error = Error>>>
        where I: IntoIterator<Item = &'a str>
    {
        let id = if let Some(id) = path.into_iter().next() {
            id
        } else {
            return Ok(ok(Self::not_found()).boxed());
        };

        let checksum = Checksum::from_str(id).map_err(|_| ErrorKind::BadRequest(BAD_OBJECT_ID))?;

        if let Some(len) = req.headers().get::<ContentLength>() {
            if len.0 > self.max_file_size {
                return Err(ErrorKind::BadRequest("file too large").into());
            }
        } else {
            return Err(ErrorKind::BadRequest("missing content-length").into());
        }

        let encoding = Self::pick_encoding(req.headers());

        info!("Creating temporary file");
        let body = io::stream_to_file(tempfile::tempfile()?, self.pool.clone(), req.body());
        Ok(self.put_uploaded_object(body, checksum, encoding))
    }

    fn inner_call<'a, I>(&self,
                         req: Request,
                         path: I)
                         -> Result<Box<Future<Item = Response, Error = Error>>>
        where I: IntoIterator<Item = &'a str>
    {
        let mut it = path.into_iter();

        if let Some(part) = it.next() {
            match (req.method(), part) {
                (&Method::Get, "objects") => return self.get_objects(it),
                (&Method::Put, "objects") => return self.put_objects(req, it),
                _ => return Ok(ok(Self::not_found()).boxed()),
            }
        }

        Ok(ok(Self::not_found()).boxed())
    }

    fn handle_error(e: Error) -> Response {
        match *e.kind() {
            ErrorKind::BadRequest(ref message) => {
                return Response::new()
                    .with_status(StatusCode::BadRequest)
                    .with_header(ContentLength(message.len() as u64))
                    .with_header(ContentType(mime::TEXT_PLAIN))
                    .with_body(*message)
            }
            _ => {
                error!("{}", e);

                for e in e.iter().skip(1) {
                    error!("caused by: {}", e);
                }

                if let Some(backtrace) = e.backtrace() {
                    error!("{:?}", backtrace);
                }

                return Response::new().with_status(StatusCode::InternalServerError);
            }
        }
    }
}

impl Service for ReprotoService {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Response, Error = hyper::Error>>;

    fn call(&self, req: Request) -> Self::Future {
        let full_path = String::from(req.path());

        let path = full_path.split('/').skip(1);

        Box::new(self.inner_call(req, path)
            .unwrap_or_else(|e| ok(Self::handle_error(e)).boxed())
            .or_else(|e| ok(Self::handle_error(e))))
    }
}
<|endoftext|>