<commit_before>use crate::utils::span_lint;
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use std::f64::consts as f64;
use syntax::ast::{FloatTy, LitKind};
use syntax::symbol;

declare_clippy_lint! {
    /// **What it does:** Checks for floating point literals that approximate
    /// constants which are defined in
    /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
    /// or
    /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
    /// respectively, suggesting to use the predefined constant.
    ///
    /// **Why is this bad?** Usually, the definition in the standard library is more
    /// precise than what people come up with. If you find that your definition is
    /// actually more precise, please [file a Rust
    /// issue](https://github.com/rust-lang/rust/issues).
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let x = 3.14;
    /// ```
    pub APPROX_CONSTANT,
    correctness,
    "the approximate of a known float constant (in `std::fXX::consts`)"
}

// Tuples are of the form (constant, name, min_digits)
const KNOWN_CONSTS: [(f64, &str, usize); 16] = [
    (f64::E, "E", 4),
    (f64::FRAC_1_PI, "FRAC_1_PI", 4),
    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
    (f64::FRAC_2_PI, "FRAC_2_PI", 5),
    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
    (f64::FRAC_PI_2, "FRAC_PI_2", 5),
    (f64::FRAC_PI_3, "FRAC_PI_3", 5),
    (f64::FRAC_PI_4, "FRAC_PI_4", 5),
    (f64::FRAC_PI_6, "FRAC_PI_6", 5),
    (f64::FRAC_PI_8, "FRAC_PI_8", 5),
    (f64::LN_10, "LN_10", 5),
    (f64::LN_2, "LN_2", 5),
    (f64::LOG10_E, "LOG10_E", 5),
    (f64::LOG2_E, "LOG2_E", 5),
    (f64::PI, "PI", 3),
    (f64::SQRT_2, "SQRT_2", 5),
];

declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ApproxConstant {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        if let ExprKind::Lit(lit) = &e.kind {
            check_lit(cx, &lit.node, e);
        }
    }
}

fn check_lit(cx: &LateContext<'_, '_>, lit: &LitKind, e: &Expr) {
    match *lit {
        LitKind::Float(s, FloatTy::F32) => check_known_consts(cx, e, s, "f32"),
        LitKind::Float(s, FloatTy::F64) => check_known_consts(cx, e, s, "f64"),
        LitKind::FloatUnsuffixed(s) => check_known_consts(cx, e, s, "f{32, 64}"),
        _ => (),
    }
}

fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr, s: symbol::Symbol, module: &str) {
    let s = s.as_str();
    if s.parse::<f64>().is_ok() {
        for &(constant, name, min_digits) in &KNOWN_CONSTS {
            if is_approx_const(constant, &s, min_digits) {
                span_lint(
                    cx,
                    APPROX_CONSTANT,
                    e.span,
                    &format!(
                        "approximate value of `{}::consts::{}` found. \
                         Consider using it directly",
                        module, &name
                    ),
                );
                return;
            }
        }
    }
}

/// Returns `false` if the number of significant figures in `value` are
/// less than `min_digits`; otherwise, returns true if `value` is equal
/// to `constant`, rounded to the number of digits present in `value`.
#[must_use]
fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
    if value.len() <= min_digits {
        false
    } else {
        let round_const = format!("{:.*}", value.len() - 2, constant);

        let mut trunc_const = constant.to_string();
        if trunc_const.len() > value.len() {
            trunc_const.truncate(value.len());
        }

        (value == round_const) || (value == trunc_const)
    }
}
<commit_msg>Simplify approx const truncation check<commit_after>use crate::utils::span_lint;
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use std::f64::consts as f64;
use syntax::ast::{FloatTy, LitKind};
use syntax::symbol;

declare_clippy_lint! {
    /// **What it does:** Checks for floating point literals that approximate
    /// constants which are defined in
    /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
    /// or
    /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
    /// respectively, suggesting to use the predefined constant.
    ///
    /// **Why is this bad?** Usually, the definition in the standard library is more
    /// precise than what people come up with. If you find that your definition is
    /// actually more precise, please [file a Rust
    /// issue](https://github.com/rust-lang/rust/issues).
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let x = 3.14;
    /// ```
    pub APPROX_CONSTANT,
    correctness,
    "the approximate of a known float constant (in `std::fXX::consts`)"
}

// Tuples are of the form (constant, name, min_digits)
const KNOWN_CONSTS: [(f64, &str, usize); 16] = [
    (f64::E, "E", 4),
    (f64::FRAC_1_PI, "FRAC_1_PI", 4),
    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
    (f64::FRAC_2_PI, "FRAC_2_PI", 5),
    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
    (f64::FRAC_PI_2, "FRAC_PI_2", 5),
    (f64::FRAC_PI_3, "FRAC_PI_3", 5),
    (f64::FRAC_PI_4, "FRAC_PI_4", 5),
    (f64::FRAC_PI_6, "FRAC_PI_6", 5),
    (f64::FRAC_PI_8, "FRAC_PI_8", 5),
    (f64::LN_10, "LN_10", 5),
    (f64::LN_2, "LN_2", 5),
    (f64::LOG10_E, "LOG10_E", 5),
    (f64::LOG2_E, "LOG2_E", 5),
    (f64::PI, "PI", 3),
    (f64::SQRT_2, "SQRT_2", 5),
];

declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ApproxConstant {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        if let ExprKind::Lit(lit) = &e.kind {
            check_lit(cx, &lit.node, e);
        }
    }
}

fn check_lit(cx: &LateContext<'_, '_>, lit: &LitKind, e: &Expr) {
    match *lit {
        LitKind::Float(s, FloatTy::F32) => check_known_consts(cx, e, s, "f32"),
        LitKind::Float(s, FloatTy::F64) => check_known_consts(cx, e, s, "f64"),
        LitKind::FloatUnsuffixed(s) => check_known_consts(cx, e, s, "f{32, 64}"),
        _ => (),
    }
}

fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr, s: symbol::Symbol, module: &str) {
    let s = s.as_str();
    if s.parse::<f64>().is_ok() {
        for &(constant, name, min_digits) in &KNOWN_CONSTS {
            if is_approx_const(constant, &s, min_digits) {
                span_lint(
                    cx,
                    APPROX_CONSTANT,
                    e.span,
                    &format!(
                        "approximate value of `{}::consts::{}` found. \
                         Consider using it directly",
                        module, &name
                    ),
                );
                return;
            }
        }
    }
}

/// Returns `false` if the number of significant figures in `value` are
/// less than `min_digits`; otherwise, returns true if `value` is equal
/// to `constant`, rounded to the number of digits present in `value`.
#[must_use]
fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
    if value.len() <= min_digits {
        false
    } else if constant.to_string().starts_with(value) {
        // The value is a truncated constant
        true
    } else {
        let round_const = format!("{:.*}", value.len() - 2, constant);
        value == round_const
    }
}
<|endoftext|>


<commit_before>use std::{
    io,
    net::{IpAddr, SocketAddr},
    sync::Arc,
    time::Duration,
};

use tokio::sync::mpsc;

#[cfg(not(target_os = "android"))]
use std::net::{Ipv4Addr, SocketAddrV4};
#[cfg(not(target_os = "android"))]
use tokio::net::UdpSocket;

use log::{debug, error, info, warn};
use trust_dns_proto::{
    op::{header::MessageType, response_code::ResponseCode, Message, Query},
    rr::RData,
};

use crate::{
    config::ConfigType,
    context::SharedContext,
    relay::{
        loadbalancing::server::{PlainPingBalancer, ServerType},
        sys::create_udp_socket,
        utils::try_timeout,
    },
};

mod upstream;

async fn acl_lookup<Local, Remote>(
    context: SharedContext,
    local: Arc<Local>,
    remote: Arc<Remote>,
    query: &Query
) -> io::Result<(Message, bool)>
    where
        Local: upstream::Upstream,
        Remote: upstream::Upstream,
{
    // Start querying name servers
    debug!(
        "attempting lookup of {:?} {} with ns {:?} and {:?}",
        query.query_type(), query.name(), local, remote
    );

    let qname_in_proxy_list = context.check_query_in_proxy_list(query);

    let remote_response_fut = async {
        match qname_in_proxy_list {
            Some(false) => None,
            _ => {
                let timeout = Some(Duration::new(3, 0));
                try_timeout(remote.lookup(query), timeout)
                    .await
                    .ok()
            }
        }
    };

    let local_response = match qname_in_proxy_list {
        Some(true) => None,
        _ => {
            let timeout = Some(Duration::new(3, 0));
            try_timeout(local.lookup(query), timeout).await.ok()
        }
    }
    .unwrap_or_else(Message::new);

    match qname_in_proxy_list {
        Some(true) => {
            let remote_response = remote_response_fut.await.unwrap_or_else(Message::new);
            debug!("pick remote response (qname): {:?}", remote_response);
            return Ok((remote_response, true));
        }
        Some(false) => {
            debug!("pick local response (qname): {:?}", local_response);
            return Ok((local_response, false));
        }
        None => (),
    }

    if local_response.answer_count() == 0 {
        let remote_response = remote_response_fut.await.unwrap_or_else(Message::new);
        return Ok((remote_response, true));
    }

    for rec in local_response.answers() {
        if rec.record_type() != query.query_type() {
            warn!("local DNS response has inconsistent answer type {} for query {}", rec.record_type(), query);
            break
        }
        let forward = match rec.rdata() {
            RData::A(ref ip) => context.check_ip_in_proxy_list(&IpAddr::from(*ip)),
            RData::AAAA(ref ip) => context.check_ip_in_proxy_list(&IpAddr::from(*ip)),
            RData::PTR(_) => panic!("PTR records should not reach here"),
            _ => context.is_default_in_proxy_list(),
        };
        if !forward {
            debug!("pick local response (response): {:?}", local_response);
            return Ok((local_response, false));
        }
    }

    let remote_response = remote_response_fut.await.unwrap_or_else(Message::new);
    debug!("pick remote response (response): {:?}", remote_response);
    Ok((remote_response, true))
}

/// Start a DNS relay local server
pub async fn run(context: SharedContext) -> io::Result<()> {
    let local_addr = match context.config().config_type {
        ConfigType::DnsLocal => {
            // Standalone server
            context.config().local_addr.as_ref().expect("local config")
        }
        c if c.is_local() => {
            // Integrated mode
            context.config().dns_local_addr.as_ref().expect("dns relay addr")
        }
        _ => {
            panic!("ConfigType must be DnsLocal");
        }
    };

    let bind_addr = local_addr.bind_addr(&context).await?;

    let socket = create_udp_socket(&bind_addr).await?;

    let actual_local_addr = socket.local_addr()?;
    info!("shadowsocks DNS relay listening on {}", actual_local_addr);

    let (mut rx, mut tx) = socket.split();
    let (qtx, mut qrx) = mpsc::channel::<(SocketAddr, Vec<u8>)>(1024);

    tokio::spawn(async move {
        while let Some((src, pkt)) = qrx.recv().await {
            if let Err(err) = tx.send_to(&pkt, &src).await {
                error!("failed to send packet {} bytes to {}, error: {}", pkt.len(), src, err);
            }
        }
    });

    let config = context.config();
    #[cfg(target_os = "android")]
    let local_upstream = Arc::new(upstream::UnixSocketUpstream {
        path: config.local_dns_path.clone().expect("local query DNS path"),
    });
    #[cfg(not(target_os = "android"))]
    let local_upstream = Arc::new(upstream::UdpUpstream {
        server: config.local_dns_addr.clone().expect("local query DNS address"),
    });
    // FIXME: We use TCP to send remote queries by default, which should be configuable.
    let balancer = PlainPingBalancer::new(context.clone(), ServerType::Tcp).await;
    let remote_upstream = Arc::new(upstream::ProxyTcpUpstream {
        context: context.clone(),
        svr_cfg: move || balancer.pick_server().server_config().clone(),
        ns: config.remote_dns_addr.clone().expect("remote query DNS address"),
    });

    loop {
        let mut req_buffer: [u8; 512] = [0; 512];
        let (_, src) = match rx.recv_from(&mut req_buffer).await {
            Ok(x) => x,
            Err(e) => {
                error!("DNS relay read from UDP socket error: {}", e);
                continue;
            }
        };

        let request = match Message::from_vec(&req_buffer) {
            Ok(x) => x,
            Err(e) => {
                error!("failed to parse UDP query message, error: {:?}", e);
                continue;
            }
        };

        debug!("received src: {}, query: {:?}", src, request);

        let context = context.clone();
        let local_upstream = Arc::clone(&local_upstream);
        let remote_upstream = Arc::clone(&remote_upstream);
        let mut qtx = qtx.clone();

        tokio::spawn(async move {
            let mut message = Message::new();
            message.set_id(request.id());
            message.set_recursion_desired(true);
            message.set_recursion_available(true);
            message.set_message_type(MessageType::Response);

            if request.queries().is_empty() {
                message.set_response_code(ResponseCode::FormErr);
            } else {
                let question = &request.queries()[0];
                let r = acl_lookup(context.clone(), local_upstream, remote_upstream, question).await;

                if let Ok((result, forward)) = r {
                    for rec in result.answers() {
                        debug!("dns answer: {:?}", rec);

                        match rec.rdata() {
                            RData::A(ref ip) => context.add_to_reverse_lookup_cache(&IpAddr::from(*ip), forward),
                            RData::AAAA(ref ip) => context.add_to_reverse_lookup_cache(&IpAddr::from(*ip), forward),
                            _ => (),
                        }
                    }

                    message = result;
                    message.set_id(request.id());
                } else {
                    message.set_response_code(ResponseCode::ServFail);
                }
            }

            debug!("DNS src: {}, final response: {:?}", src, message);

            match message.to_vec() {
                Err(err) => {
                    error!("failed to serialize message, error: {}", err);
                }
                Ok(res_buffer) => {
                    if let Err(..) = qtx.send((src, res_buffer)).await {
                        error!("DNS send back queue is closed unexpectly");
                    }
                }
            }
        });
    }
}
<commit_msg>Refine the futures<commit_after>use std::{
    io,
    net::{IpAddr, SocketAddr},
    sync::Arc,
    time::Duration,
};

use tokio::sync::mpsc;

#[cfg(not(target_os = "android"))]
use std::net::{Ipv4Addr, SocketAddrV4};
#[cfg(not(target_os = "android"))]
use tokio::net::UdpSocket;

use log::{debug, error, info, warn};
use trust_dns_proto::{
    op::{header::MessageType, response_code::ResponseCode, Message, Query},
    rr::RData,
};

use crate::{
    config::ConfigType,
    context::SharedContext,
    relay::{
        loadbalancing::server::{PlainPingBalancer, ServerType},
        sys::create_udp_socket,
        utils::try_timeout,
    },
};

mod upstream;

async fn acl_lookup<Local, Remote>(
    context: SharedContext,
    local: Arc<Local>,
    remote: Arc<Remote>,
    query: &Query
) -> io::Result<(Message, bool)>
    where
        Local: upstream::Upstream,
        Remote: upstream::Upstream,
{
    // Start querying name servers
    debug!(
        "attempting lookup of {:?} {} with ns {:?} and {:?}",
        query.query_type(), query.name(), local, remote
    );

    let qname_in_proxy_list = context.check_query_in_proxy_list(query);
    let remote_response_fut = try_timeout(remote.lookup(query), Some(Duration::new(3, 0)));
    let local_response_fut = try_timeout(local.lookup(query), Some(Duration::new(3, 0)));

    match qname_in_proxy_list {
        Some(true) => {
            let remote_response = remote_response_fut.await.unwrap_or_else(|_| Message::new());
            debug!("pick remote response (qname): {:?}", remote_response);
            return Ok((remote_response, true));
        }
        Some(false) => {
            let local_response = local_response_fut.await.unwrap_or_else(|_| Message::new());
            debug!("pick local response (qname): {:?}", local_response);
            return Ok((local_response, false));
        }
        None => (),
    }

    let local_response = local_response_fut.await.unwrap_or_else(|_| Message::new());
    for rec in local_response.answers() {
        if rec.record_type() != query.query_type() {
            warn!("local DNS response has inconsistent answer type {} for query {}", rec.record_type(), query);
            break
        }
        let forward = match rec.rdata() {
            RData::A(ref ip) => context.check_ip_in_proxy_list(&IpAddr::from(*ip)),
            RData::AAAA(ref ip) => context.check_ip_in_proxy_list(&IpAddr::from(*ip)),
            RData::PTR(_) => panic!("PTR records should not reach here"),
            _ => context.is_default_in_proxy_list(),
        };
        if !forward {
            debug!("pick local response (response): {:?}", local_response);
            return Ok((local_response, false));
        }
    }

    let remote_response = remote_response_fut.await.unwrap_or_else(|_| Message::new());
    debug!("pick remote response (response): {:?}", remote_response);
    Ok((remote_response, true))
}

/// Start a DNS relay local server
pub async fn run(context: SharedContext) -> io::Result<()> {
    let local_addr = match context.config().config_type {
        ConfigType::DnsLocal => {
            // Standalone server
            context.config().local_addr.as_ref().expect("local config")
        }
        c if c.is_local() => {
            // Integrated mode
            context.config().dns_local_addr.as_ref().expect("dns relay addr")
        }
        _ => {
            panic!("ConfigType must be DnsLocal");
        }
    };

    let bind_addr = local_addr.bind_addr(&context).await?;

    let socket = create_udp_socket(&bind_addr).await?;

    let actual_local_addr = socket.local_addr()?;
    info!("shadowsocks DNS relay listening on {}", actual_local_addr);

    let (mut rx, mut tx) = socket.split();
    let (qtx, mut qrx) = mpsc::channel::<(SocketAddr, Vec<u8>)>(1024);

    tokio::spawn(async move {
        while let Some((src, pkt)) = qrx.recv().await {
            if let Err(err) = tx.send_to(&pkt, &src).await {
                error!("failed to send packet {} bytes to {}, error: {}", pkt.len(), src, err);
            }
        }
    });

    let config = context.config();
    #[cfg(target_os = "android")]
    let local_upstream = Arc::new(upstream::UnixSocketUpstream {
        path: config.local_dns_path.clone().expect("local query DNS path"),
    });
    #[cfg(not(target_os = "android"))]
    let local_upstream = Arc::new(upstream::UdpUpstream {
        server: config.local_dns_addr.clone().expect("local query DNS address"),
    });
    // FIXME: We use TCP to send remote queries by default, which should be configuable.
    let balancer = PlainPingBalancer::new(context.clone(), ServerType::Tcp).await;
    let remote_upstream = Arc::new(upstream::ProxyTcpUpstream {
        context: context.clone(),
        svr_cfg: move || balancer.pick_server().server_config().clone(),
        ns: config.remote_dns_addr.clone().expect("remote query DNS address"),
    });

    loop {
        let mut req_buffer: [u8; 512] = [0; 512];
        let (_, src) = match rx.recv_from(&mut req_buffer).await {
            Ok(x) => x,
            Err(e) => {
                error!("DNS relay read from UDP socket error: {}", e);
                continue;
            }
        };

        let request = match Message::from_vec(&req_buffer) {
            Ok(x) => x,
            Err(e) => {
                error!("failed to parse UDP query message, error: {:?}", e);
                continue;
            }
        };

        debug!("received src: {}, query: {:?}", src, request);

        let context = context.clone();
        let local_upstream = Arc::clone(&local_upstream);
        let remote_upstream = Arc::clone(&remote_upstream);
        let mut qtx = qtx.clone();

        tokio::spawn(async move {
            let mut message = Message::new();
            message.set_id(request.id());
            message.set_recursion_desired(true);
            message.set_recursion_available(true);
            message.set_message_type(MessageType::Response);

            if request.queries().is_empty() {
                message.set_response_code(ResponseCode::FormErr);
            } else {
                let question = &request.queries()[0];
                let r = acl_lookup(context.clone(), local_upstream, remote_upstream, question).await;

                if let Ok((result, forward)) = r {
                    for rec in result.answers() {
                        debug!("dns answer: {:?}", rec);

                        match rec.rdata() {
                            RData::A(ref ip) => context.add_to_reverse_lookup_cache(&IpAddr::from(*ip), forward),
                            RData::AAAA(ref ip) => context.add_to_reverse_lookup_cache(&IpAddr::from(*ip), forward),
                            _ => (),
                        }
                    }

                    message = result;
                    message.set_id(request.id());
                } else {
                    message.set_response_code(ResponseCode::ServFail);
                }
            }

            debug!("DNS src: {}, final response: {:?}", src, message);

            match message.to_vec() {
                Err(err) => {
                    error!("failed to serialize message, error: {}", err);
                }
                Ok(res_buffer) => {
                    if let Err(..) = qtx.send((src, res_buffer)).await {
                        error!("DNS send back queue is closed unexpectly");
                    }
                }
            }
        });
    }
}
<|endoftext|>


<commit_before>use std::convert::From;

use binding::string::{from_value, new};
use types::Value;

use super::traits::Object;

/// `String`
pub struct RString {
    value: Value
}

impl RString {
    /// Creates a new instance of Ruby `String` containing given `string`.
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{RString, VM};
    /// # VM::init();
    ///
    /// let string = RString::new("Hello, World!");
    ///
    /// assert_eq!(string.to_string(), "Hello, World!".to_string());
    /// ```
    pub fn new(string: &str) -> Self {
        RString {
            value: new(string)
        }
    }

    /// Retrieves underlying Rust `String` from Ruby `String` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{RString, VM};
    /// # VM::init();
    ///
    /// let string = RString::new("Hello, World!");
    ///
    /// assert_eq!(string.to_string(), "Hello, World!".to_string());
    /// ```
    pub fn to_string(&self) -> String {
        from_value(self.value)
    }
}

impl From<Value> for RString {
    fn from(value: Value) -> Self {
        RString {
            value: value
        }
    }
}

impl Object for RString {
    fn value(&self) -> Value {
        self.value
    }
}
<commit_msg>Add Ruby examples for RString<commit_after>use std::convert::From;

use binding::string::{from_value, new};
use types::Value;

use super::traits::Object;

/// `String`
pub struct RString {
    value: Value
}

impl RString {
    /// Creates a new instance of Ruby `String` containing given `string`.
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{RString, VM};
    /// # VM::init();
    ///
    /// let string = RString::new("Hello, World!");
    ///
    /// assert_eq!(string.to_string(), "Hello, World!".to_string());
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// str = 'Hello, World!'
    ///
    /// str == 'Hello, World!'
    /// ```
    pub fn new(string: &str) -> Self {
        RString {
            value: new(string)
        }
    }

    /// Retrieves underlying Rust `String` from Ruby `String` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{RString, VM};
    /// # VM::init();
    ///
    /// let string = RString::new("Hello, World!");
    ///
    /// assert_eq!(string.to_string(), "Hello, World!".to_string());
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// str = 'Hello, World!'
    ///
    /// str == 'Hello, World!'
    /// ```
    pub fn to_string(&self) -> String {
        from_value(self.value)
    }
}

impl From<Value> for RString {
    fn from(value: Value) -> Self {
        RString {
            value: value
        }
    }
}

impl Object for RString {
    fn value(&self) -> Value {
        self.value
    }
}
<|endoftext|>


<commit_before>// $ cargo bench --features full --bench rust
//
// Syn only, useful for profiling:
// $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full --bench rust

#![cfg_attr(not(syn_only), feature(rustc_private))]

#[cfg(not(syn_only))]
extern crate rustc_data_structures;
#[cfg(not(syn_only))]
extern crate syntax;
#[cfg(not(syn_only))]
extern crate syntax_pos;

#[path = "../tests/repo/mod.rs"]
mod repo;

#[cfg(not(syn_only))]
use proc_macro2::TokenStream;
#[cfg(not(syn_only))]
use rustc_data_structures::sync::Lrc;
#[cfg(not(syn_only))]
use std::str::FromStr;
#[cfg(not(syn_only))]
use syntax::edition::Edition;
#[cfg(not(syn_only))]
use syntax::errors::{emitter::Emitter, DiagnosticBuilder, Handler};
#[cfg(not(syn_only))]
use syntax::parse::ParseSess;
#[cfg(not(syn_only))]
use syntax::source_map::{FilePathMapping, SourceMap};
#[cfg(not(syn_only))]
use syntax_pos::FileName;

use std::fs;
use std::time::{Duration, Instant};

#[cfg(not(syn_only))]
fn tokenstream_parse(content: &str) -> Result<(), ()> {
    TokenStream::from_str(content).map(drop).map_err(drop)
}

fn syn_parse(content: &str) -> Result<(), ()> {
    syn::parse_file(content).map(drop).map_err(drop)
}

#[cfg(not(syn_only))]
fn libsyntax_parse(content: &str) -> Result<(), ()> {
    struct SilentEmitter;

    impl Emitter for SilentEmitter {
        fn emit_diagnostic(&mut self, _db: &DiagnosticBuilder) {}
    }

    syntax::with_globals(Edition::Edition2018, || {
        let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
        let emitter = Box::new(SilentEmitter);
        let handler = Handler::with_emitter(false, None, emitter);
        let sess = ParseSess::with_span_handler(handler, cm);
        if let Err(mut diagnostic) = syntax::parse::parse_crate_from_source_str(
            FileName::Custom("bench".to_owned()),
            content.to_owned(),
            &sess,
        ) {
            diagnostic.cancel();
            return Err(());
        };
        Ok(())
    })
}

#[cfg(not(syn_only))]
fn read_from_disk(content: &str) -> Result<(), ()> {
    let _ = content;
    Ok(())
}

fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
    let begin = Instant::now();
    let mut success = 0;
    let mut total = 0;

    walkdir::WalkDir::new("tests/rust/src")
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .for_each(|entry| {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                return;
            }
            let content = fs::read_to_string(path).unwrap();
            let ok = codepath(&content).is_ok();
            success += ok as usize;
            total += 1;
            if !ok {
                eprintln!("FAIL {}", path.display());
            }
        });

    assert_eq!(success, total);
    begin.elapsed()
}

fn main() {
    repo::clone_rust();

    macro_rules! testcases {
        ($($(#[$cfg:meta])* $name:ident,)*) => {
            vec![
                $(
                    $(#[$cfg])*
                    (stringify!($name), $name as fn(&str) -> Result<(), ()>),
                )*
            ]
        };
    }

    #[cfg(not(syn_only))]
    {
        let mut lines = 0;
        let mut files = 0;
        exec(|content| {
            lines += content.lines().count();
            files += 1;
            Ok(())
        });
        eprintln!("\n{} lines in {} files", lines, files);
    }

    for (name, f) in testcases!(
        #[cfg(not(syn_only))]
        read_from_disk,
        #[cfg(not(syn_only))]
        tokenstream_parse,
        syn_parse,
        #[cfg(not(syn_only))]
        libsyntax_parse,
    ) {
        eprint!("{:20}", format!("{}:", name));
        let elapsed = exec(f);
        eprintln!(
            "elapsed={}.{:03}s",
            elapsed.as_secs(),
            elapsed.subsec_millis(),
        );
    }
    eprintln!();
}
<commit_msg>Group the non-syn benchmark imports<commit_after>// $ cargo bench --features full --bench rust
//
// Syn only, useful for profiling:
// $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full --bench rust

#![cfg_attr(not(syn_only), feature(rustc_private))]

#[path = "../tests/repo/mod.rs"]
mod repo;

#[cfg(not(syn_only))]
mod imports {
    pub extern crate rustc_data_structures;
    pub extern crate syntax;
    pub extern crate syntax_pos;

    pub use proc_macro2::TokenStream;
    pub use rustc_data_structures::sync::Lrc;
    pub use std::str::FromStr;
    pub use syntax::edition::Edition;
    pub use syntax::errors::{emitter::Emitter, DiagnosticBuilder, Handler};
    pub use syntax::parse::ParseSess;
    pub use syntax::source_map::{FilePathMapping, SourceMap};
    pub use syntax_pos::FileName;
}

#[cfg(not(syn_only))]
use imports::*;

use std::fs;
use std::time::{Duration, Instant};

#[cfg(not(syn_only))]
fn tokenstream_parse(content: &str) -> Result<(), ()> {
    TokenStream::from_str(content).map(drop).map_err(drop)
}

fn syn_parse(content: &str) -> Result<(), ()> {
    syn::parse_file(content).map(drop).map_err(drop)
}

#[cfg(not(syn_only))]
fn libsyntax_parse(content: &str) -> Result<(), ()> {
    struct SilentEmitter;

    impl Emitter for SilentEmitter {
        fn emit_diagnostic(&mut self, _db: &DiagnosticBuilder) {}
    }

    syntax::with_globals(Edition::Edition2018, || {
        let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
        let emitter = Box::new(SilentEmitter);
        let handler = Handler::with_emitter(false, None, emitter);
        let sess = ParseSess::with_span_handler(handler, cm);
        if let Err(mut diagnostic) = syntax::parse::parse_crate_from_source_str(
            FileName::Custom("bench".to_owned()),
            content.to_owned(),
            &sess,
        ) {
            diagnostic.cancel();
            return Err(());
        };
        Ok(())
    })
}

#[cfg(not(syn_only))]
fn read_from_disk(content: &str) -> Result<(), ()> {
    let _ = content;
    Ok(())
}

fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
    let begin = Instant::now();
    let mut success = 0;
    let mut total = 0;

    walkdir::WalkDir::new("tests/rust/src")
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .for_each(|entry| {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                return;
            }
            let content = fs::read_to_string(path).unwrap();
            let ok = codepath(&content).is_ok();
            success += ok as usize;
            total += 1;
            if !ok {
                eprintln!("FAIL {}", path.display());
            }
        });

    assert_eq!(success, total);
    begin.elapsed()
}

fn main() {
    repo::clone_rust();

    macro_rules! testcases {
        ($($(#[$cfg:meta])* $name:ident,)*) => {
            vec![
                $(
                    $(#[$cfg])*
                    (stringify!($name), $name as fn(&str) -> Result<(), ()>),
                )*
            ]
        };
    }

    #[cfg(not(syn_only))]
    {
        let mut lines = 0;
        let mut files = 0;
        exec(|content| {
            lines += content.lines().count();
            files += 1;
            Ok(())
        });
        eprintln!("\n{} lines in {} files", lines, files);
    }

    for (name, f) in testcases!(
        #[cfg(not(syn_only))]
        read_from_disk,
        #[cfg(not(syn_only))]
        tokenstream_parse,
        syn_parse,
        #[cfg(not(syn_only))]
        libsyntax_parse,
    ) {
        eprint!("{:20}", format!("{}:", name));
        let elapsed = exec(f);
        eprintln!(
            "elapsed={}.{:03}s",
            elapsed.as_secs(),
            elapsed.subsec_millis(),
        );
    }
    eprintln!();
}
<|endoftext|>


<commit_before>use Options;
use std::io::{Read, Write};
use std::sync::{Arc, RwLock};
use std::thread::sleep_ms;
use time::strftime;
use hyper::client::Client;
use hyper::method::Method;
use bear_lib_terminal::terminal;
use bear_lib_terminal::geometry::{Size, Rect, Point};
use chattium_oxide_lib::ChatMessage;
use chattium_oxide_lib::json::{ToJsonnable, FromJsonnable, JsonError};

pub struct ResponseRequester {
	server    : String,
	client    : Arc<Client>,
	keep_going: Arc<RwLock<bool>>,
	messages  : Vec<ChatMessage>,
}

impl ResponseRequester {
	pub fn new(options: Options, client: Arc<Client>, keep_going: Arc<RwLock<bool>>) -> ResponseRequester {
		ResponseRequester{
			server    : options.server,
			client    : client,
			keep_going: keep_going,
			messages  : Vec::new(),
		}
	}

	pub fn call(mut self) {
		self.draw_line();

		while *self.keep_going.read().unwrap() {
			self.draw_line();

			match self.request_message() {
				Ok(json) =>
					match self.client.request(Method::Trace, &*&self.server).body(&*&json).send() {
						Ok(mut res) => {
							let mut resbody = String::new();
							match res.read_to_string(&mut resbody) {
								Ok(_) =>
									match Vec::<ChatMessage>::from_json_string(&resbody) {
										Ok(ref mut messages) => {
											self.messages.append(messages);
											self.print_messages();
										},
										Err(error) => printerr!("Server at {} replied with invalid JSON: {}\n", self.server, error),
									},
								Err(error) => printerr!("Failed reading request from server at {}: {}\n", res.url, error),
							}
						},
						Err(error) => printerr!("GETing new (before #{}) messages failed: {}\n", json, error),
					},
				Err(error) => printerr!("Couldn't serialize message: {}\n", error),
			}

			sleep_ms(500);
		}
	}

	fn print_messages(&self) {
		let size = terminal::state::size();

		if size.height != 0 {  // Terminal dead otherwise
			terminal::clear(Some(Rect::from_size(Point::new(0, 0), Size::new(size.width, size.height - 2))));
			for (i, message) in self.messages.iter().rev().take((size.height - 2) as usize).enumerate() {
				terminal::print_xy(0, size.height - 3 - i as i32,
				                   &*&format!("{} | {}: {}", strftime("%T", &message.time_posted).unwrap(), message.sender.name, message.value));
			}
			terminal::refresh();
		}
	}

	fn draw_line(&self) {
		let size = terminal::state::size();
		for x in 0..size.width {
			terminal::put_xy(x, size.height - 2, '—')
		}
		terminal::refresh();
	}

	fn request_message(&self) -> Result<String, JsonError> {
		self.messages.iter().rev().next().map(|ref m| m.id).unwrap_or(0).to_json_string()
	}
}
<commit_msg>Color-code own messages<commit_after>use Options;
use std::io::{Read, Write};
use std::sync::{Arc, RwLock};
use std::thread::sleep_ms;
use time::strftime;
use hyper::client::Client;
use hyper::method::Method;
use bear_lib_terminal::{terminal, Color};
use bear_lib_terminal::geometry::{Size, Rect, Point};
use chattium_oxide_lib::{ChatMessage, ChatUser};
use chattium_oxide_lib::json::{ToJsonnable, FromJsonnable, JsonError};

pub struct ResponseRequester {
	server    : String,
	me        : ChatUser,
	client    : Arc<Client>,
	keep_going: Arc<RwLock<bool>>,
	messages  : Vec<ChatMessage>,
}

impl ResponseRequester {
	pub fn new(options: Options, client: Arc<Client>, keep_going: Arc<RwLock<bool>>) -> ResponseRequester {
		ResponseRequester{
			server    : options.server,
			me        : ChatUser::me(options.name),
			client    : client,
			keep_going: keep_going,
			messages  : Vec::new(),
		}
	}

	pub fn call(mut self) {
		self.draw_line();

		while *self.keep_going.read().unwrap() {
			self.draw_line();

			match self.request_message() {
				Ok(json) =>
					match self.client.request(Method::Trace, &*&self.server).body(&*&json).send() {
						Ok(mut res) => {
							let mut resbody = String::new();
							match res.read_to_string(&mut resbody) {
								Ok(_) =>
									match Vec::<ChatMessage>::from_json_string(&resbody) {
										Ok(ref mut messages) => {
											self.messages.append(messages);
											self.print_messages();
										},
										Err(error) => printerr!("Server at {} replied with invalid JSON: {}\n", self.server, error),
									},
								Err(error) => printerr!("Failed reading request from server at {}: {}\n", res.url, error),
							}
						},
						Err(error) => printerr!("GETing new (before #{}) messages failed: {}\n", json, error),
					},
				Err(error) => printerr!("Couldn't serialize message: {}\n", error),
			}

			sleep_ms(500);
		}
	}

	fn print_messages(&self) {
		let size = terminal::state::size();

		if size.height != 0 {  // Terminal dead otherwise
			terminal::clear(Some(Rect::from_size(Point::new(0, 0), Size::new(size.width, size.height - 2))));
			for (i, message) in self.messages.iter().rev().take((size.height - 2) as usize).enumerate() {
				let is_own = message.sender == self.me;

				if is_own {
					terminal::set_foreground(Color::from_rgb(221, 221, 221));
				}

				terminal::print_xy(0, size.height - 3 - i as i32,
				                   &*&format!("{} | {}: {}", strftime("%T", &message.time_posted).unwrap(), message.sender.name, message.value));

				if is_own {
					terminal::set_foreground(Color::from_rgb(255, 255, 255));
				}
			}
			terminal::refresh();
		}
	}

	fn draw_line(&self) {
		let size = terminal::state::size();
		for x in 0..size.width {
			terminal::put_xy(x, size.height - 2, '—')
		}
		terminal::refresh();
	}

	fn request_message(&self) -> Result<String, JsonError> {
		self.messages.iter().rev().next().map(|ref m| m.id).unwrap_or(0).to_json_string()
	}
}
<|endoftext|>


<commit_before>use prelude::*;

cfg_if! {
    if #[cfg(feature = "std")] {
        use std::fmt;
        use std::str;
    } else {
        use core::fmt;
        use core::str;
    }
}

impl fmt::Display for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::LowerHex::fmt(self, f)
    }
}

impl fmt::Display for UuidVariant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            UuidVariant::NCS => write!(f, "NCS"),
            UuidVariant::RFC4122 => write!(f, "RFC4122"),
            UuidVariant::Microsoft => write!(f, "Microsoft"),
            UuidVariant::Future => write!(f, "Future"),
        }
    }
}

impl fmt::LowerHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::LowerHex>::fmt(&self.hyphenated(), f)
    }
}

impl fmt::UpperHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::LowerHex>::fmt(&self.hyphenated(), f)
    }
}

impl str::FromStr for Uuid {
    type Err = super::ParseError;

    fn from_str(uuid_str: &str) -> Result<Uuid, super::ParseError> {
        Uuid::parse_str(uuid_str)
    }
}

impl Default for Uuid {
    fn default() -> Self {
        Uuid::nil()
    }
}

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

    #[test]
    fn test_uuid_compare() {
        let uuid1 = test_util::new();
        let uuid2 = test_util::new2();

        assert_eq!(uuid1, uuid1);
        assert_eq!(uuid2, uuid2);

        assert_ne!(uuid1, uuid2);
        assert_ne!(uuid2, uuid1);
    }

    #[test]
    fn test_uuid_default() {
        let default_uuid = Uuid::default();
        let nil_uuid = Uuid::nil();

        assert_eq!(default_uuid, nil_uuid);
    }

    #[test]
    fn test_uuid_display() {
        let uuid = test_util::new();
        let s = uuid.to_string();

        assert_eq!(s, uuid.hyphenated().to_string())
    }

    #[test]
    fn test_uuid_to_string() {
        let uuid = test_util::new();
        let s = uuid.to_string();

        assert_eq!(s.len(), 36);
        assert!(s.chars().all(|c| c.is_digit(16) || c == '-'))
    }

    #[test]
    fn test_uuid_operator_eq() {
        let uuid1 = test_util::new();
        let uuid1_dup = uuid1.clone();
        let uuid2 = test_util::new();

        assert!(uuid1 == uuid1);
        assert!(uuid1 == uuid1_dup);
        assert!(uuid1_dup == uuid1);

        assert!(uuid1 != uuid2);
        assert!(uuid2 != uuid1);
        assert!(uuid1_dup != uuid2);
        assert!(uuid2 != uuid1_dup);
    }
}
<commit_msg>fix test_uuid_operator_eq error<commit_after>use prelude::*;

cfg_if! {
    if #[cfg(feature = "std")] {
        use std::fmt;
        use std::str;
    } else {
        use core::fmt;
        use core::str;
    }
}

impl fmt::Display for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::LowerHex::fmt(self, f)
    }
}

impl fmt::Display for UuidVariant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            UuidVariant::NCS => write!(f, "NCS"),
            UuidVariant::RFC4122 => write!(f, "RFC4122"),
            UuidVariant::Microsoft => write!(f, "Microsoft"),
            UuidVariant::Future => write!(f, "Future"),
        }
    }
}

impl fmt::LowerHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::LowerHex>::fmt(&self.hyphenated(), f)
    }
}

impl fmt::UpperHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::LowerHex>::fmt(&self.hyphenated(), f)
    }
}

impl str::FromStr for Uuid {
    type Err = super::ParseError;

    fn from_str(uuid_str: &str) -> Result<Uuid, super::ParseError> {
        Uuid::parse_str(uuid_str)
    }
}

impl Default for Uuid {
    fn default() -> Self {
        Uuid::nil()
    }
}

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

    #[test]
    fn test_uuid_compare() {
        let uuid1 = test_util::new();
        let uuid2 = test_util::new2();

        assert_eq!(uuid1, uuid1);
        assert_eq!(uuid2, uuid2);

        assert_ne!(uuid1, uuid2);
        assert_ne!(uuid2, uuid1);
    }

    #[test]
    fn test_uuid_default() {
        let default_uuid = Uuid::default();
        let nil_uuid = Uuid::nil();

        assert_eq!(default_uuid, nil_uuid);
    }

    #[test]
    fn test_uuid_display() {
        let uuid = test_util::new();
        let s = uuid.to_string();

        assert_eq!(s, uuid.hyphenated().to_string())
    }

    #[test]
    fn test_uuid_to_string() {
        let uuid = test_util::new();
        let s = uuid.to_string();

        assert_eq!(s.len(), 36);
        assert!(s.chars().all(|c| c.is_digit(16) || c == '-'))
    }

    #[test]
    fn test_uuid_operator_eq() {
        let uuid1 = test_util::new();
        let uuid1_dup = uuid1.clone();
        let uuid2 = test_util::new2();

        assert!(uuid1 == uuid1);
        assert!(uuid1 == uuid1_dup);
        assert!(uuid1_dup == uuid1);

        assert!(uuid1 != uuid2);
        assert!(uuid2 != uuid1);
        assert!(uuid1_dup != uuid2);
        assert!(uuid2 != uuid1_dup);
    }
}
<|endoftext|>


<commit_before>//! `CuckooFilter` implementation.
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::hash::{BuildHasher, Hash, Hasher};
use std::mem;

use rand::Rng;

use hash_utils::MyBuildHasherDefault;

// TODO: make fingerprint type a type param of CuckooFilter
type Fingerprint = u8;

const MAX_NUM_KICKS: usize = 500; // mentioned in paper

/// Error struct used to signal that a `CuckooFilter` is full, i.e. that a value cannot be inserted
/// because the implementation was unable to find a free bucket.
#[derive(Debug)]
pub struct CuckooFilterFull;

/// [`CuckooFilter`](https://www.cs.cmu.edu/~dga/papers/cuckoo-conext2014.pdf).
#[derive(Clone)]
pub struct CuckooFilter<R, B = MyBuildHasherDefault<DefaultHasher>>
where
    R: Rng,
    B: BuildHasher + Clone + Eq,
{
    table: Vec<Fingerprint>,
    n_elements: usize,
    buildhasher: B,
    bucketsize: usize,
    n_buckets: usize,
    rng: R,
}

impl<R> CuckooFilter<R>
where
    R: Rng,
{
    /// Create new CuckooFilter with:
    ///
    /// - `rng`: random number generator used for certain random actions
    /// - `bucketsize`: number of elements per bucket, must be at least 2
    /// - `n_buckets`: number of buckets, must be a power of 2 and at least 2
    ///
    /// The BuildHasher is set to the `DefaultHasher`.
    pub fn with_params(rng: R, bucketsize: usize, n_buckets: usize) -> Self {
        let bh = MyBuildHasherDefault::<DefaultHasher>::default();
        Self::with_params_and_hash(rng, bucketsize, n_buckets, bh)
    }
}

impl<R, B> CuckooFilter<R, B>
where
    R: Rng,
    B: BuildHasher + Clone + Eq,
{
    /// Create new CuckooFilter with:
    ///
    /// - `rng`: random number generator used for certain random actions
    /// - `bucketsize`: number of elements per bucket, must be at least 2
    /// - `n_buckets`: number of buckets, must be a power of 2 and at least 2
    /// - `bh`: BuildHasher that creates Hash objects, used for fingerprint creation and
    ///   fingerprint hashing
    pub fn with_params_and_hash(rng: R, bucketsize: usize, n_buckets: usize, bh: B) -> Self {
        assert!(
            bucketsize >= 2,
            "bucketsize ({}) should be greater or equal than 2",
            bucketsize
        );
        assert!(
            n_buckets.is_power_of_two() & (n_buckets >= 2),
            "n_buckets ({}) must be a power of 2 and greater or equal than 2",
            n_buckets
        );

        Self {
            table: vec![0; n_buckets * bucketsize],
            n_elements: 0,
            buildhasher: bh,
            bucketsize: bucketsize,
            n_buckets: n_buckets,
            rng: rng,
        }
    }

    /// Number of entries stored in a bucket.
    pub fn bucketsize(&self) -> usize {
        self.bucketsize
    }

    /// Number of buckets used by the CuckooFilter.
    pub fn n_buckets(&self) -> usize {
        self.n_buckets
    }

    /// Check if CuckooFilter is empty, i.e. contains no elements.
    pub fn empty(&self) -> bool {
        self.n_elements == 0
    }

    /// Return number of elements in the filter.
    pub fn len(&self) -> usize {
        self.n_elements
    }

    /// Insert new element into filter.
    ///
    /// The method may return an error if it was unable to find a free bucket. This means the
    /// filter is full and you should not add any additional elements to it. When this happens,
    /// `len` was not increased and the filter content was not altered.
    ///
    /// Inserting the same element multiple times is supported, but keep in mind that after
    /// `n_buckets * 2` times, the filter will return `Err(CuckooFilterFull)`.
    pub fn insert<T>(&mut self, t: &T) -> Result<(), CuckooFilterFull>
    where
        T: Hash,
    {
        let (mut f, i1, i2) = self.start(t);

        if self.write_to_bucket(i1, f) {
            self.n_elements += 1;
            return Ok(());
        }
        if self.write_to_bucket(i2, f) {
            self.n_elements += 1;
            return Ok(());
        }

        // cannot write to obvious buckets => relocate
        let table_backup = self.table.clone(); // may be required for rollback
        let mut i = if self.rng.gen::<bool>() { i1 } else { i2 };

        for _ in 0..MAX_NUM_KICKS {
            let e = self.rng.gen_range::<usize>(0, self.bucketsize);
            let offset = i * self.bucketsize;
            let x = offset + e;

            mem::swap(&mut self.table[x], &mut f);
            i = i ^ self.hash(&f);
            if self.write_to_bucket(i, f) {
                self.n_elements += 1;
                return Ok(());
            }
        }

        // no space left => fail
        self.table = table_backup; // rollback transaction
        Err(CuckooFilterFull)
    }

    /// Check if given element was inserted into the filter.
    pub fn lookup<T>(&self, t: &T) -> bool
    where
        T: Hash,
    {
        let (f, i1, i2) = self.start(t);

        if self.has_in_bucket(i1, f) {
            return true;
        }
        if self.has_in_bucket(i2, f) {
            return true;
        }
        false
    }

    /// Remove element from the filter.
    ///
    /// Returns `true` if element was in the filter, `false` if it was not in which case the operation did not modify
    /// the filter.
    pub fn delete<T>(&mut self, t: &T) -> bool
    where
        T: Hash,
    {
        let (f, i1, i2) = self.start(t);

        if self.remove_from_bucket(i1, f) {
            self.n_elements -= 1;
            return true;
        }
        if self.remove_from_bucket(i2, f) {
            self.n_elements -= 1;
            return true;
        }
        false
    }

    fn start<T>(&self, t: &T) -> (Fingerprint, usize, usize)
    where
        T: Hash,
    {
        let f = self.fingerprint(t);
        let i1 = self.hash(t);
        let i2 = i1 ^ self.hash(&f);
        (f, i1, i2)
    }

    fn fingerprint<T>(&self, t: &T) -> Fingerprint
    where
        T: Hash,
    {
        let mut hasher = self.buildhasher.build_hasher();
        hasher.write_usize(0); // IV
        t.hash(&mut hasher);

        // don't produce 0, since this is used as "free"-slot value
        1 + (hasher.finish() % (Fingerprint::max_value() as u64 - 1)) as Fingerprint
    }

    fn hash<T>(&self, t: &T) -> usize
    where
        T: Hash,
    {
        let mut hasher = self.buildhasher.build_hasher();
        hasher.write_usize(1); // IV
        t.hash(&mut hasher);
        (hasher.finish() & (self.n_buckets as u64 - 1)) as usize
    }

    fn write_to_bucket(&mut self, i: usize, f: Fingerprint) -> bool {
        let offset = i * self.bucketsize;
        for x in offset..(offset + self.bucketsize) {
            if self.table[x] == 0 {
                self.table[x] = f;
                return true;
            }
        }
        false
    }

    fn has_in_bucket(&self, i: usize, f: Fingerprint) -> bool {
        let offset = i * self.bucketsize;
        for x in offset..(offset + self.bucketsize) {
            if self.table[x] == f {
                return true;
            }
        }
        false
    }

    fn remove_from_bucket(&mut self, i: usize, f: Fingerprint) -> bool {
        let offset = i * self.bucketsize;
        for x in offset..(offset + self.bucketsize) {
            if self.table[x] == f {
                self.table[x] = 0;
                return true;
            }
        }
        false
    }
}

impl<R, B> fmt::Debug for CuckooFilter<R, B>
where
    R: Rng,
    B: BuildHasher + Clone + Eq,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "CuckooFilter {{ bucketsize: {}, n_buckets: {} }}",
            self.bucketsize, self.n_buckets
        )
    }
}

#[cfg(test)]
mod tests {
    use super::CuckooFilter;
    use rand::{ChaChaRng, SeedableRng};

    #[test]
    #[should_panic(expected = "bucketsize (0) should be greater or equal than 2")]
    fn new_panics_bucketsize_0() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 0, 16);
    }

    #[test]
    #[should_panic(expected = "bucketsize (1) should be greater or equal than 2")]
    fn new_panics_bucketsize_1() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 1, 16);
    }

    #[test]
    #[should_panic(expected = "n_buckets (0) must be a power of 2 and greater or equal than 2")]
    fn new_panics_n_buckets_0() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 0);
    }

    #[test]
    #[should_panic(expected = "n_buckets (1) must be a power of 2 and greater or equal than 2")]
    fn new_panics_n_buckets_1() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 1);
    }

    #[test]
    #[should_panic(expected = "n_buckets (5) must be a power of 2 and greater or equal than 2")]
    fn new_panics_n_buckets_5() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 5);
    }

    #[test]
    fn getter() {
        let cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16);
        assert_eq!(cf.bucketsize(), 2);
        assert_eq!(cf.n_buckets(), 16);
    }

    #[test]
    fn empty() {
        let cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16);
        assert!(cf.empty());
        assert_eq!(cf.len(), 0);
    }

    #[test]
    fn insert() {
        let mut cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16);
        cf.insert(&13).unwrap();
        assert!(!cf.empty());
        assert_eq!(cf.len(), 1);
        assert!(cf.lookup(&13));
        assert!(!cf.lookup(&42));
    }

    #[test]
    fn delete() {
        let mut cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16);
        cf.insert(&13).unwrap();
        cf.insert(&42).unwrap();
        assert!(cf.lookup(&13));
        assert!(cf.lookup(&42));
        assert_eq!(cf.len(), 2);

        assert!(cf.delete(&13));
        assert!(!cf.lookup(&13));
        assert!(cf.lookup(&42));
        assert_eq!(cf.len(), 1);
    }

    #[test]
    fn full() {
        let mut cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 2);

        for i in 0..4 {
            cf.insert(&i).unwrap();
        }
        assert_eq!(cf.len(), 4);
        for i in 0..4 {
            assert!(cf.lookup(&i));
        }

        assert!(cf.insert(&5).is_err());
        assert_eq!(cf.len(), 4);
        assert!(!cf.lookup(&5)); // rollback was executed
    }

    #[test]
    fn debug() {
        let cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16);
        assert_eq!(
            format!("{:?}", cf),
            "CuckooFilter { bucketsize: 2, n_buckets: 16 }"
        );
    }

    #[test]
    fn clone() {
        let mut cf1 = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16);
        cf1.insert(&13).unwrap();
        assert!(cf1.lookup(&13));

        let cf2 = cf1.clone();
        cf1.insert(&42).unwrap();
        assert!(cf2.lookup(&13));
        assert!(!cf2.lookup(&42));
    }
}
<commit_msg>cuckoofilter: variable fingerprint size<commit_after>//! `CuckooFilter` implementation.
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::hash::{BuildHasher, Hash, Hasher};

use fixedbitset::FixedBitSet;
use rand::Rng;

use hash_utils::MyBuildHasherDefault;

const MAX_NUM_KICKS: usize = 500; // mentioned in paper

/// Error struct used to signal that a `CuckooFilter` is full, i.e. that a value cannot be inserted
/// because the implementation was unable to find a free bucket.
#[derive(Debug)]
pub struct CuckooFilterFull;

/// [`CuckooFilter`](https://www.cs.cmu.edu/~dga/papers/cuckoo-conext2014.pdf).
#[derive(Clone)]
pub struct CuckooFilter<R, B = MyBuildHasherDefault<DefaultHasher>>
where
    R: Rng,
    B: BuildHasher + Clone + Eq,
{
    table: FixedBitSet,
    n_elements: usize,
    buildhasher: B,
    bucketsize: usize,
    n_buckets: usize,
    l_fingerprint: usize,
    rng: R,
}

impl<R> CuckooFilter<R>
where
    R: Rng,
{
    /// Create new CuckooFilter with:
    ///
    /// - `rng`: random number generator used for certain random actions
    /// - `bucketsize`: number of elements per bucket, must be at least 2
    /// - `n_buckets`: number of buckets, must be a power of 2 and at least 2
    /// - `l_fingerprint`: size of the fingerprint in bits
    ///
    /// The BuildHasher is set to the `DefaultHasher`.
    pub fn with_params(rng: R, bucketsize: usize, n_buckets: usize, l_fingerprint: usize) -> Self {
        let bh = MyBuildHasherDefault::<DefaultHasher>::default();
        Self::with_params_and_hash(rng, bucketsize, n_buckets, l_fingerprint, bh)
    }
}

impl<R, B> CuckooFilter<R, B>
where
    R: Rng,
    B: BuildHasher + Clone + Eq,
{
    /// Create new CuckooFilter with:
    ///
    /// - `rng`: random number generator used for certain random actions
    /// - `bucketsize`: number of elements per bucket, must be at least 2
    /// - `n_buckets`: number of buckets, must be a power of 2 and at least 2
    /// - `l_fingerprint`: size of the fingerprint in bits
    /// - `bh`: BuildHasher that creates Hash objects, used for fingerprint creation and
    ///   fingerprint hashing
    pub fn with_params_and_hash(
        rng: R,
        bucketsize: usize,
        n_buckets: usize,
        l_fingerprint: usize,
        bh: B,
    ) -> Self {
        assert!(
            bucketsize >= 2,
            "bucketsize ({}) must be greater or equal than 2",
            bucketsize
        );
        assert!(
            n_buckets.is_power_of_two() & (n_buckets >= 2),
            "n_buckets ({}) must be a power of 2 and greater or equal than 2",
            n_buckets
        );
        assert!(
            (l_fingerprint > 1) & (l_fingerprint <= 64),
            "l_fingerprint ({}) must be greater than 1 and less or equal than 64",
            l_fingerprint
        );

        let table_size = n_buckets
            .checked_mul(bucketsize)
            .expect("Table size too large")
            .checked_mul(l_fingerprint)
            .expect("Table size too large");

        Self {
            table: FixedBitSet::with_capacity(table_size),
            n_elements: 0,
            buildhasher: bh,
            bucketsize: bucketsize,
            n_buckets: n_buckets,
            l_fingerprint: l_fingerprint,
            rng: rng,
        }
    }

    /// Number of entries stored in a bucket.
    pub fn bucketsize(&self) -> usize {
        self.bucketsize
    }

    /// Number of buckets used by the CuckooFilter.
    pub fn n_buckets(&self) -> usize {
        self.n_buckets
    }

    /// Size of the used fingerprint in bits
    pub fn l_fingerprint(&self) -> usize {
        self.l_fingerprint
    }

    /// Check if CuckooFilter is empty, i.e. contains no elements.
    pub fn empty(&self) -> bool {
        self.n_elements == 0
    }

    /// Return number of elements in the filter.
    pub fn len(&self) -> usize {
        self.n_elements
    }

    /// Insert new element into filter.
    ///
    /// The method may return an error if it was unable to find a free bucket. This means the
    /// filter is full and you should not add any additional elements to it. When this happens,
    /// `len` was not increased and the filter content was not altered.
    ///
    /// Inserting the same element multiple times is supported, but keep in mind that after
    /// `n_buckets * 2` times, the filter will return `Err(CuckooFilterFull)`.
    pub fn insert<T>(&mut self, t: &T) -> Result<(), CuckooFilterFull>
    where
        T: Hash,
    {
        let (mut f, i1, i2) = self.start(t);

        if self.write_to_bucket(i1, f) {
            self.n_elements += 1;
            return Ok(());
        }
        if self.write_to_bucket(i2, f) {
            self.n_elements += 1;
            return Ok(());
        }

        // cannot write to obvious buckets => relocate
        let table_backup = self.table.clone(); // may be required for rollback
        let mut i = if self.rng.gen::<bool>() { i1 } else { i2 };

        for _ in 0..MAX_NUM_KICKS {
            let e = self.rng.gen_range::<usize>(0, self.bucketsize);
            let offset = i * self.bucketsize;
            let x = offset + e;

            // swap table[x] and f
            let tmp = self.table_read(x);
            self.table_write(x, f);
            f = tmp;

            i = i ^ self.hash(&f);
            if self.write_to_bucket(i, f) {
                self.n_elements += 1;
                return Ok(());
            }
        }

        // no space left => fail
        self.table = table_backup; // rollback transaction
        Err(CuckooFilterFull)
    }

    /// Check if given element was inserted into the filter.
    pub fn lookup<T>(&self, t: &T) -> bool
    where
        T: Hash,
    {
        let (f, i1, i2) = self.start(t);

        if self.has_in_bucket(i1, f) {
            return true;
        }
        if self.has_in_bucket(i2, f) {
            return true;
        }
        false
    }

    /// Remove element from the filter.
    ///
    /// Returns `true` if element was in the filter, `false` if it was not in which case the operation did not modify
    /// the filter.
    pub fn delete<T>(&mut self, t: &T) -> bool
    where
        T: Hash,
    {
        let (f, i1, i2) = self.start(t);

        if self.remove_from_bucket(i1, f) {
            self.n_elements -= 1;
            return true;
        }
        if self.remove_from_bucket(i2, f) {
            self.n_elements -= 1;
            return true;
        }
        false
    }

    fn start<T>(&self, t: &T) -> (u64, usize, usize)
    where
        T: Hash,
    {
        let f = self.fingerprint(t);
        let i1 = self.hash(t);
        let i2 = i1 ^ self.hash(&f);
        (f, i1, i2)
    }

    fn fingerprint<T>(&self, t: &T) -> u64
    where
        T: Hash,
    {
        let mut hasher = self.buildhasher.build_hasher();
        hasher.write_usize(0); // IV
        t.hash(&mut hasher);

        // don't produce 0, since this is used as "free"-slot value
        let x_mod = if self.l_fingerprint == 64 {
            u64::max_value()
        } else {
            (1u64 << self.l_fingerprint) - 1
        };
        1 + (hasher.finish() % x_mod)
    }

    fn hash<T>(&self, t: &T) -> usize
    where
        T: Hash,
    {
        let mut hasher = self.buildhasher.build_hasher();
        hasher.write_usize(1); // IV
        t.hash(&mut hasher);
        (hasher.finish() & (self.n_buckets as u64 - 1)) as usize
    }

    fn write_to_bucket(&mut self, i: usize, f: u64) -> bool {
        let offset = i * self.bucketsize;
        for x in offset..(offset + self.bucketsize) {
            if self.table_read(x) == 0 {
                self.table_write(x, f);
                return true;
            }
        }
        false
    }

    fn has_in_bucket(&self, i: usize, f: u64) -> bool {
        let offset = i * self.bucketsize;
        for x in offset..(offset + self.bucketsize) {
            if self.table_read(x) == f {
                return true;
            }
        }
        false
    }

    fn remove_from_bucket(&mut self, i: usize, f: u64) -> bool {
        let offset = i * self.bucketsize;
        for x in offset..(offset + self.bucketsize) {
            if self.table_read(x) == f {
                self.table_write(x, 0);
                return true;
            }
        }
        false
    }

    fn table_read(&self, i: usize) -> u64 {
        let offset = i * self.l_fingerprint;
        let mut result = 0u64;
        for b in 0..self.l_fingerprint {
            result |= (self.table[offset + b] as u64) << (self.l_fingerprint - b - 1);
        }
        result
    }

    fn table_write(&mut self, i: usize, f: u64) {
        let offset = i * self.l_fingerprint;
        for b in 0..self.l_fingerprint {
            self.table
                .set(offset + b, (f >> (self.l_fingerprint - b - 1) & 1u64) != 0);
        }
    }
}

impl<R, B> fmt::Debug for CuckooFilter<R, B>
where
    R: Rng,
    B: BuildHasher + Clone + Eq,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "CuckooFilter {{ bucketsize: {}, n_buckets: {} }}",
            self.bucketsize, self.n_buckets
        )
    }
}

#[cfg(test)]
mod tests {
    use super::CuckooFilter;
    use rand::{ChaChaRng, SeedableRng};

    #[test]
    #[should_panic(expected = "bucketsize (0) must be greater or equal than 2")]
    fn new_panics_bucketsize_0() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 0, 16, 8);
    }

    #[test]
    #[should_panic(expected = "bucketsize (1) must be greater or equal than 2")]
    fn new_panics_bucketsize_1() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 1, 16, 8);
    }

    #[test]
    #[should_panic(expected = "n_buckets (0) must be a power of 2 and greater or equal than 2")]
    fn new_panics_n_buckets_0() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 0, 8);
    }

    #[test]
    #[should_panic(expected = "n_buckets (1) must be a power of 2 and greater or equal than 2")]
    fn new_panics_n_buckets_1() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 1, 8);
    }

    #[test]
    #[should_panic(expected = "n_buckets (5) must be a power of 2 and greater or equal than 2")]
    fn new_panics_n_buckets_5() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 5, 8);
    }

    #[test]
    #[should_panic(expected = "l_fingerprint (0) must be greater than 1 and less or equal than 64")]
    fn new_panics_l_fingerprint_0() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16, 0);
    }

    #[test]
    #[should_panic(expected = "l_fingerprint (1) must be greater than 1 and less or equal than 64")]
    fn new_panics_l_fingerprint_1() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16, 1);
    }

    #[test]
    #[should_panic(
        expected = "l_fingerprint (65) must be greater than 1 and less or equal than 64"
    )]
    fn new_panics_l_fingerprint_65() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16, 65);
    }

    #[test]
    #[should_panic(expected = "Table size too large")]
    fn new_panics_table_size_overflow_1() {
        CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), usize::max_value(), 2, 2);
    }

    #[test]
    #[should_panic(expected = "Table size too large")]
    fn new_panics_table_size_overflow_2() {
        CuckooFilter::with_params(
            ChaChaRng::from_seed([0; 32]),
            2,
            (((usize::max_value() as u128) + 1) / 2) as usize,
            2,
        );
    }

    #[test]
    #[should_panic(expected = "Table size too large")]
    fn new_panics_table_size_overflow_3() {
        CuckooFilter::with_params(
            ChaChaRng::from_seed([0; 32]),
            2,
            (((usize::max_value() as u128) + 1) / 8) as usize,
            64,
        );
    }

    #[test]
    fn getter() {
        let cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16, 8);
        assert_eq!(cf.bucketsize(), 2);
        assert_eq!(cf.n_buckets(), 16);
        assert_eq!(cf.l_fingerprint(), 8);
    }

    #[test]
    fn empty() {
        let cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16, 8);
        assert!(cf.empty());
        assert_eq!(cf.len(), 0);
    }

    #[test]
    fn insert() {
        let mut cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16, 8);
        cf.insert(&13).unwrap();
        assert!(!cf.empty());
        assert_eq!(cf.len(), 1);
        assert!(cf.lookup(&13));
        assert!(!cf.lookup(&42));
    }

    #[test]
    fn delete() {
        let mut cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16, 8);
        cf.insert(&13).unwrap();
        cf.insert(&42).unwrap();
        assert!(cf.lookup(&13));
        assert!(cf.lookup(&42));
        assert_eq!(cf.len(), 2);

        assert!(cf.delete(&13));
        assert!(!cf.lookup(&13));
        assert!(cf.lookup(&42));
        assert_eq!(cf.len(), 1);
    }

    #[test]
    fn full() {
        let mut cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 2, 8);

        for i in 0..4 {
            cf.insert(&i).unwrap();
        }
        assert_eq!(cf.len(), 4);
        for i in 0..4 {
            assert!(cf.lookup(&i));
        }

        assert!(cf.insert(&5).is_err());
        assert_eq!(cf.len(), 4);
        assert!(!cf.lookup(&5)); // rollback was executed
    }

    #[test]
    fn debug() {
        let cf = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16, 8);
        assert_eq!(
            format!("{:?}", cf),
            "CuckooFilter { bucketsize: 2, n_buckets: 16 }"
        );
    }

    #[test]
    fn clone() {
        let mut cf1 = CuckooFilter::with_params(ChaChaRng::from_seed([0; 32]), 2, 16, 8);
        cf1.insert(&13).unwrap();
        assert!(cf1.lookup(&13));

        let cf2 = cf1.clone();
        cf1.insert(&42).unwrap();
        assert!(cf2.lookup(&13));
        assert!(!cf2.lookup(&42));
    }
}
<|endoftext|>


<commit_before>use std::time::Duration;

#[derive(Default, Clone)]
struct FrameInfo {
    frame: u32,
    elapsed: Duration,
}

impl FrameInfo {
    fn new(frame: u32, elapsed: Duration) -> Self {
        FrameInfo {
            frame: frame,
            elapsed: elapsed,
        }
    }

    fn elapse(&self, elapsed: Duration) -> Self {
        FrameInfo::new(self.frame, elapsed)
    }
}

#[derive(Clone)]
pub struct FrameAnimator {
    max: u32,
    duration: Duration,
    repeat: bool,
    current: Option<FrameInfo>,
}

impl FrameAnimator {
    pub fn new(max: u32, duration: Duration, repeat: bool) -> FrameAnimator {
        FrameAnimator {
            max: max,
            duration: duration,
            repeat: repeat,
            current: None,
        }
    }

    pub fn frame(&self) -> Option<u32> {
        self.current.as_ref().map(|c| c.frame)
    }

    pub fn num_frames(&self) -> u32 {
        self.max
    }

    pub fn animate(&mut self, delta: Duration) {
        self.current = self.current.as_ref().map_or(Some(Default::default()), |frame| {
            let elapsed = frame.elapsed + delta;
            elapsed.checked_sub(self.duration)
                .map_or(Some(frame.elapse(elapsed)), |r| self.next(frame, r))
        });
    }

    fn next(&self, current: &FrameInfo, remaining: Duration) -> Option<FrameInfo> {
        let frame = (current.frame + 1) % self.max;
        if frame > 0 || self.repeat {
            Some(FrameInfo::new(frame, remaining))
        } else {
            None
        }
    }
}
<commit_msg>begin adding tests to animator now that it is testable<commit_after>use std::time::Duration;

#[derive(Default, Clone)]
struct FrameInfo {
    frame: u32,
    elapsed: Duration,
}

impl FrameInfo {
    fn new(frame: u32, elapsed: Duration) -> Self {
        FrameInfo {
            frame: frame,
            elapsed: elapsed,
        }
    }

    fn elapse(&self, elapsed: Duration) -> Self {
        FrameInfo::new(self.frame, elapsed)
    }
}

#[derive(Clone)]
pub struct FrameAnimator {
    max: u32,
    duration: Duration,
    repeat: bool,
    current: Option<FrameInfo>,
}

impl FrameAnimator {
    pub fn new(max: u32, duration: Duration, repeat: bool) -> FrameAnimator {
        FrameAnimator {
            max: max,
            duration: duration,
            repeat: repeat,
            current: None,
        }
    }

    pub fn started(max: u32, duration: Duration, repeat: bool) -> FrameAnimator {
        FrameAnimator {
            max: max,
            duration: duration,
            repeat: repeat,
            current: Some(Default::default()),
        }
    }

    pub fn start(&mut self) {
        self.current = Some(Default::default());
    }

    pub fn frame(&self) -> Option<u32> {
        self.current.as_ref().map(|c| c.frame)
    }

    pub fn num_frames(&self) -> u32 {
        self.max
    }

    pub fn animate(&mut self, delta: Duration) {
        self.current = self.current.as_ref().map_or(Some(Default::default()), |frame| {
            let elapsed = frame.elapsed + delta;
            elapsed.checked_sub(self.duration)
                .map_or(Some(frame.elapse(elapsed)), |r| self.next(frame, r))
        });
    }

    fn next(&self, current: &FrameInfo, remaining: Duration) -> Option<FrameInfo> {
        let frame = (current.frame + 1) % self.max;
        if frame > 0 || self.repeat {
            Some(FrameInfo::new(frame, remaining))
        } else {
            None
        }
    }
}

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

    #[test]
    fn new_animator() {
        let animator = FrameAnimator::new(3, Duration::from_secs(5), true);
        assert!(animator.frame().is_none());
    }

    #[test]
    fn started_animator() {
        let animator = FrameAnimator::started(3, Duration::from_secs(5), true);
        assert!(animator.frame().is_some());
        assert_eq!(animator.frame().unwrap(), 0);
    }

    #[test]
    fn start() {
        let mut animator = FrameAnimator::new(3, Duration::from_secs(5), true);
        animator.start();
        assert!(animator.frame().is_some());
        assert_eq!(animator.frame().unwrap(), 0);
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt;
use std::fs;
use std::io::Read;
use std::path;

use toml;

use utils::ServerError;

#[derive(Clone)]
pub struct Config {
    pub listen: String,
    pub data_path: path::PathBuf
}

impl Config {
    pub fn read_file(path: &path::Path) -> Result<Self, ServerError> {
        let mut s = String::new();
        let mut f = try!(fs::File::open(path));
        try!(f.read_to_string(&mut s));

        let mut parser = toml::Parser::new(&s);
        let mut sections = match parser.parse() {
            Some(x) => x,
            None => return Err(ConfigError::new(format!("{:?}", parser.errors)).into())
        };

        let mut main_section = match sections.remove("main") {
            Some(toml::Value::Table(x)) => x,
            _ => panic!("Config file is missing `main` section.")
        };

        let listen = match main_section.remove("listen") {
            Some(toml::Value::String(x)) => x,
            _ => panic!("The `listen` parameter is missing.")
        };

        let data_path = match main_section.remove("data_path") {
            Some(toml::Value::String(x)) => path.join(&x),
            _ => panic!("The `data_path` parameter is missing.")
        };

        Ok(Config {
            listen: listen,
            data_path: data_path
        })
    }
}

#[derive(Debug)]
pub struct ConfigError {
    pub msg: String
}

impl ConfigError {
    pub fn new(msg: String) -> Self {
        ConfigError { msg: msg }
    }
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(f)
    }
}

impl Error for ConfigError {
    fn description(&self) -> &str { &self.msg[..] }
    fn cause(&self) -> Option<&Error> { None }
}

impl From<toml::ParserError> for ServerError {
    fn from(e: toml::ParserError) -> ServerError {
        ServerError { error: Box::new(e) }
    }
}

impl From<ConfigError> for ServerError {
    fn from(e: ConfigError) -> ServerError {
        ServerError { error: Box::new(e) }
    }
}

<commit_msg>Allow relative paths in config<commit_after>use std::error::Error;
use std::fmt;
use std::env;
use std::fs;
use std::io::Read;
use std::path;

use toml;

use utils::ServerError;

#[derive(Clone)]
pub struct Config {
    pub listen: String,
    pub data_path: path::PathBuf
}

impl Config {
    pub fn read_file(path: &path::Path) -> Result<Self, ServerError> {
        let path = &env::current_dir().unwrap().join(path);
        let mut s = String::new();
        let mut f = try!(fs::File::open(path));
        try!(f.read_to_string(&mut s));

        let mut parser = toml::Parser::new(&s);
        let mut sections = match parser.parse() {
            Some(x) => x,
            None => return Err(ConfigError::new(format!("{:?}", parser.errors)).into())
        };

        let mut main_section = match sections.remove("main") {
            Some(toml::Value::Table(x)) => x,
            _ => panic!("Config file is missing `main` section.")
        };

        let listen = match main_section.remove("listen") {
            Some(toml::Value::String(x)) => x,
            _ => panic!("The `listen` parameter is missing.")
        };

        let data_path = match main_section.remove("data_path") {
            Some(toml::Value::String(x)) => path.join(&x),
            _ => panic!("The `data_path` parameter is missing.")
        };

        Ok(Config {
            listen: listen,
            data_path: data_path
        })
    }
}

#[derive(Debug)]
pub struct ConfigError {
    pub msg: String
}

impl ConfigError {
    pub fn new(msg: String) -> Self {
        ConfigError { msg: msg }
    }
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(f)
    }
}

impl Error for ConfigError {
    fn description(&self) -> &str { &self.msg[..] }
    fn cause(&self) -> Option<&Error> { None }
}

impl From<toml::ParserError> for ServerError {
    fn from(e: toml::ParserError) -> ServerError {
        ServerError { error: Box::new(e) }
    }
}

impl From<ConfigError> for ServerError {
    fn from(e: ConfigError) -> ServerError {
        ServerError { error: Box::new(e) }
    }
}

<|endoftext|>


<commit_before>use test::*;
use mock::*;

macro_rules! test_prim_encode {
    ($({ $testname:ident, $iter:expr, $variant:ident, $T:ty }),*) => {$(
        #[test]
        fn $testname() {
            for val in $iter {
                let mut protocol = encode(&val);
                assert_eq!(protocol.log(), &[Prim($variant(val.clone()))]);
                assert_eq!(decode::<$T>(&mut protocol), val);
            }
        }
    )*}
}

test_prim_encode! {
    { test_i8_encode,  (0..100), Byte, i8 },
    { test_i16_encode, (0..100), I16, i16 },
    { test_i32_encode, (0..100), I32, i32 },
    { test_i64_encode, (0..100), I64, i64 },
    { test_string_encode, vec![
        String::from("hello"),
        String::from("goodbye"),
        String::from("garbage"),
        String::from("unicode \u{2600}\u{2601}")
    ], PString, String }
}

<commit_msg>Add tests for container encoding and decoding.<commit_after>use test::*;
use mock::*;

use protocol::Type;

macro_rules! test_prim_encode {
    ($({ $testname:ident, $iter:expr, $variant:ident, $T:ty }),*) => {$(
        #[test]
        fn $testname() {
            for val in $iter {
                let mut protocol = encode(&val);
                assert_eq!(protocol.log(), &[Prim($variant(val.clone()))]);
                assert_eq!(decode::<$T>(&mut protocol), val);
            }
        }
    )*}
}

test_prim_encode! {
    { test_i8_encode,  (0..100), Byte, i8 },
    { test_i16_encode, (0..100), I16, i16 },
    { test_i32_encode, (0..100), I32, i32 },
    { test_i64_encode, (0..100), I64, i64 },
    { test_string_encode, vec![
        String::from("hello"),
        String::from("goodbye"),
        String::from("garbage"),
        String::from("unicode \u{2600}\u{2601}")
    ], PString, String }
}

#[test]
fn test_list() {
    let list = vec![5, 6, 2, 3, 45, 56, 6];
    let mut protocol = encode(&list);

    assert_eq!(protocol.log(), &[
        List(Begin((Type::I32, list.len()))),
        Prim(I32(5)),
        Prim(I32(6)),
        Prim(I32(2)),
        Prim(I32(3)),
        Prim(I32(45)),
        Prim(I32(56)),
        Prim(I32(6)),
        List(End)
    ]);

    assert_eq!(list, decode::<Vec<i32>>(&mut protocol));
}

<|endoftext|>