<commit_before>use std::fmt;
use std::collections::HashMap;
use std::io::{Read, Write, BufRead, BufReader};

use client::Stats;
use error::MemcacheError;
use stream::Stream;
use value::{ToMemcacheValue, FromMemcacheValue};

#[derive(Default)]
pub struct Options {
    pub noreply: bool,
    pub exptime: u32,
    pub flags: u32,
}

enum StoreCommand {
    Set,
    Add,
    Replace,
    Append,
    Prepend,
}

impl fmt::Display for StoreCommand {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            StoreCommand::Set => write!(f, "set"),
            StoreCommand::Add => write!(f, "add"),
            StoreCommand::Replace => write!(f, "replace"),
            StoreCommand::Append => write!(f, "append"),
            StoreCommand::Prepend => write!(f, "prepend"),
        }
    }
}
pub struct AsciiProtocol<C: Read + Write + Sized> {
    pub reader: BufReader<C>,
}

impl AsciiProtocol<Stream> {
    fn store<V: ToMemcacheValue<Stream>>(
        &mut self,
        command: StoreCommand,
        key: &str,
        value: V,
        options: &Options,
    ) -> Result<(), MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }

        let mut header = format!(
            "{} {} {} {} {}",
            command,
            key,
            value.get_flags(),
            options.exptime,
            value.get_length()
        );
        if options.noreply {
            header += " noreply";
        }
        header += "\r\n";
        self.reader.get_mut().write(header.as_bytes())?;
        value.write_to(self.reader.get_mut())?;
        self.reader.get_mut().write(b"\r\n")?;
        self.reader.get_mut().flush()?;

        if options.noreply {
            return Ok(());
        }

        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "STORED\r\n" {
            return Ok(());
        } else if s == "NOT_STORED\r\n" {
            return Ok(());
        } else {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
    }

    pub(super) fn version(&mut self) -> Result<String, MemcacheError> {
        self.reader.get_mut().write(b"version\r\n")?;
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if !s.starts_with("VERSION") {
            return Err(MemcacheError::ServerError(0));
        }
        let s = s.trim_start_matches("VERSION ");
        let s = s.trim_end_matches("\r\n");

        return Ok(s.to_string());
    }

    pub(super) fn flush(&mut self) -> Result<(), MemcacheError> {
        match self.reader.get_mut().write(b"flush_all\r\n") {
            Ok(_) => {}
            Err(err) => return Err(MemcacheError::from(err)),
        }
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s != "OK\r\n" {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
        return Ok(());
    }

    pub(super) fn flush_with_delay(&mut self, delay: u32) -> Result<(), MemcacheError> {
        write!(self.reader.get_mut(), "flush_all {}\r\n", delay)?;
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s != "OK\r\n" {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
        return Ok(());
    }

    pub(super) fn get<V: FromMemcacheValue>(&mut self, key: &str) -> Result<Option<V>, MemcacheError> {
        write!(self.reader.get_mut(), "get {}\r\n", key)?;

        let mut s = String::new();
        let _ = self.reader.read_line(&mut s)?;

        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s.starts_with("END") {
            return Ok(None);
        } else if !s.starts_with("VALUE") {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }

        let header: Vec<_> = s.trim_end_matches("\r\n").split(" ").collect();
        if header.len() != 4 {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }

        let key = header[1];
        if key != key {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
        let flags = header[2].parse()?;
        let length = header[3].parse()?;

        let mut buffer = vec![0; length];
        self.reader.read_exact(buffer.as_mut_slice())?;

        // read the rest \r\n and END\r\n
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s)?;
        if s != "\r\n" {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
        s = String::new();
        let _ = self.reader.read_line(&mut s)?;
        if s != "END\r\n" {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }

        return Ok(Some(FromMemcacheValue::from_memcache_value(buffer, flags)?));
    }

    pub(super) fn gets<V: FromMemcacheValue>(
        &mut self,
        keys: Vec<&str>,
    ) -> Result<HashMap<String, V>, MemcacheError> {
        write!(self.reader.get_mut(), "gets {}\r\n", keys.join(" "))?;

        let mut result: HashMap<String, V> = HashMap::new();
        loop {
            let mut s = String::new();
            let _ = self.reader.read_line(&mut s)?;

            if is_memcache_error(s.as_str()) {
                return Err(MemcacheError::from(s));
            } else if s.starts_with("END") {
                break;
            } else if !s.starts_with("VALUE") {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }

            let header: Vec<_> = s.trim_end_matches("\r\n").split(" ").collect();
            if header.len() != 5 {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }

            let key = header[1];
            if key != key {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }
            let flags = header[2].parse()?;
            let length = header[3].parse()?;

            let mut buffer = vec![0; length];
            self.reader.read_exact(buffer.as_mut_slice())?;

            result.insert(key.to_string(), FromMemcacheValue::from_memcache_value(buffer, flags)?);

            // read the rest \r\n
            let mut s = String::new();
            let _ = self.reader.read_line(&mut s)?;
            if s != "\r\n" {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }
        }

        return Ok(result);
    }

    pub(super) fn set<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
        expiration: u32,
    ) -> Result<(), MemcacheError> {
        let options = Options{exptime: expiration, ..Default::default()};
        return self.store(StoreCommand::Set, key, value, &options);
    }

    pub(super) fn add<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
        expiration: u32,
    ) -> Result<(), MemcacheError> {
        let options = Options{exptime: expiration, ..Default::default()};
        return self.store(StoreCommand::Add, key, value, &options);
    }

    pub(super) fn replace<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
        expiration: u32,
    ) -> Result<(), MemcacheError> {
        let options = Options{exptime: expiration, ..Default::default()};
        return self.store(StoreCommand::Replace, key, value, &options);
    }

    pub(super) fn append<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
    ) -> Result<(), MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        return self.store(StoreCommand::Append, key, value, &Default::default());
    }

    pub(super) fn prepend<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
    ) -> Result<(), MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        return self.store(StoreCommand::Prepend, key, value, &Default::default());
    }

    pub(super) fn delete(&mut self, key: &str) -> Result<bool, MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        write!(self.reader.get_mut(), "delete {}\r\n", key)?;
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "DELETED\r\n" {
            return Ok(true);
        } else if s == "NOT_FOUND\r\n" {
            return Ok(false);
        } else {
            return Err(MemcacheError::ClientError(String::from("invalid server response")));
        }
    }

    pub(super) fn increment(&mut self, key: &str, amount: u64) -> Result<u64, MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        write!(self.reader.get_mut(), "incr {} {}\r\n", key, amount)?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "NOT_FOUND\r\n" {
            return Err(MemcacheError::from(1));
        } else {
            match s.trim_end_matches("\r\n").parse::<u64>() {
                Ok(n) => return Ok(n),
                Err(_) => return Err(MemcacheError::ClientError("invalid server response".into())),
            }
        }
    }

    pub(super) fn decrement(&mut self, key: &str, amount: u64) -> Result<u64, MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        write!(self.reader.get_mut(), "decr {} {}\r\n", key, amount)?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "NOT_FOUND\r\n" {
            return Err(MemcacheError::from(1));
        } else {
            match s.trim_end_matches("\r\n").parse::<u64>() {
                Ok(n) => return Ok(n),
                Err(_) => return Err(MemcacheError::ClientError("invalid server response".into())),
            }
        }
    }

    pub(super) fn touch(&mut self, key: &str, expiration: u32) -> Result<bool, MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        write!(self.reader.get_mut(), "touch {} {}\r\n", key, expiration)?;
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "TOUCHED\r\n" {
            return Ok(true);
        } else if s == "NOT_FOUND\r\n" {
            return Ok(false);
        } else {
            return Err(MemcacheError::ClientError(String::from("invalid server response")));
        }
    }

    pub(super) fn stats(&mut self) -> Result<Stats, MemcacheError> {
        self.reader.get_mut().write(b"stats\r\n")?;
        self.reader.get_mut().flush()?;

        let mut result: Stats = HashMap::new();
        loop {
            let mut s = String::new();
            let _ = self.reader.read_line(&mut s)?;

            if is_memcache_error(s.as_str()) {
                return Err(MemcacheError::from(s));
            } else if s.starts_with("END") {
                break;
            } else if !s.starts_with("STAT") {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }

            let stat: Vec<_> = s.trim_end_matches("\r\n").split(" ").collect();
            if stat.len() != 3 {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }
            let key = stat[1];
            let value = stat[2];
            result.insert(key.into(), value.into());
        }

        return Ok(result);
    }
}

fn is_memcache_error(s: &str) -> bool {
    return s == "ERROR\r\n" || s.starts_with("CIENT_ERROR") || s.starts_with("SERVER_ERROR");
}
<commit_msg>:bug: fix bug that stats can't handle value which contains space while using ascii protocol<commit_after>use std::fmt;
use std::collections::HashMap;
use std::io::{Read, Write, BufRead, BufReader};

use client::Stats;
use error::MemcacheError;
use stream::Stream;
use value::{ToMemcacheValue, FromMemcacheValue};

#[derive(Default)]
pub struct Options {
    pub noreply: bool,
    pub exptime: u32,
    pub flags: u32,
}

enum StoreCommand {
    Set,
    Add,
    Replace,
    Append,
    Prepend,
}

impl fmt::Display for StoreCommand {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            StoreCommand::Set => write!(f, "set"),
            StoreCommand::Add => write!(f, "add"),
            StoreCommand::Replace => write!(f, "replace"),
            StoreCommand::Append => write!(f, "append"),
            StoreCommand::Prepend => write!(f, "prepend"),
        }
    }
}
pub struct AsciiProtocol<C: Read + Write + Sized> {
    pub reader: BufReader<C>,
}

impl AsciiProtocol<Stream> {
    fn store<V: ToMemcacheValue<Stream>>(
        &mut self,
        command: StoreCommand,
        key: &str,
        value: V,
        options: &Options,
    ) -> Result<(), MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }

        let mut header = format!(
            "{} {} {} {} {}",
            command,
            key,
            value.get_flags(),
            options.exptime,
            value.get_length()
        );
        if options.noreply {
            header += " noreply";
        }
        header += "\r\n";
        self.reader.get_mut().write(header.as_bytes())?;
        value.write_to(self.reader.get_mut())?;
        self.reader.get_mut().write(b"\r\n")?;
        self.reader.get_mut().flush()?;

        if options.noreply {
            return Ok(());
        }

        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "STORED\r\n" {
            return Ok(());
        } else if s == "NOT_STORED\r\n" {
            return Ok(());
        } else {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
    }

    pub(super) fn version(&mut self) -> Result<String, MemcacheError> {
        self.reader.get_mut().write(b"version\r\n")?;
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if !s.starts_with("VERSION") {
            return Err(MemcacheError::ServerError(0));
        }
        let s = s.trim_start_matches("VERSION ");
        let s = s.trim_end_matches("\r\n");

        return Ok(s.to_string());
    }

    pub(super) fn flush(&mut self) -> Result<(), MemcacheError> {
        match self.reader.get_mut().write(b"flush_all\r\n") {
            Ok(_) => {}
            Err(err) => return Err(MemcacheError::from(err)),
        }
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s != "OK\r\n" {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
        return Ok(());
    }

    pub(super) fn flush_with_delay(&mut self, delay: u32) -> Result<(), MemcacheError> {
        write!(self.reader.get_mut(), "flush_all {}\r\n", delay)?;
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s != "OK\r\n" {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
        return Ok(());
    }

    pub(super) fn get<V: FromMemcacheValue>(&mut self, key: &str) -> Result<Option<V>, MemcacheError> {
        write!(self.reader.get_mut(), "get {}\r\n", key)?;

        let mut s = String::new();
        let _ = self.reader.read_line(&mut s)?;

        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s.starts_with("END") {
            return Ok(None);
        } else if !s.starts_with("VALUE") {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }

        let header: Vec<_> = s.trim_end_matches("\r\n").split(" ").collect();
        if header.len() != 4 {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }

        let key = header[1];
        if key != key {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
        let flags = header[2].parse()?;
        let length = header[3].parse()?;

        let mut buffer = vec![0; length];
        self.reader.read_exact(buffer.as_mut_slice())?;

        // read the rest \r\n and END\r\n
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s)?;
        if s != "\r\n" {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }
        s = String::new();
        let _ = self.reader.read_line(&mut s)?;
        if s != "END\r\n" {
            return Err(MemcacheError::ClientError("invalid server response".into()));
        }

        return Ok(Some(FromMemcacheValue::from_memcache_value(buffer, flags)?));
    }

    pub(super) fn gets<V: FromMemcacheValue>(
        &mut self,
        keys: Vec<&str>,
    ) -> Result<HashMap<String, V>, MemcacheError> {
        write!(self.reader.get_mut(), "gets {}\r\n", keys.join(" "))?;

        let mut result: HashMap<String, V> = HashMap::new();
        loop {
            let mut s = String::new();
            let _ = self.reader.read_line(&mut s)?;

            if is_memcache_error(s.as_str()) {
                return Err(MemcacheError::from(s));
            } else if s.starts_with("END") {
                break;
            } else if !s.starts_with("VALUE") {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }

            let header: Vec<_> = s.trim_end_matches("\r\n").split(" ").collect();
            if header.len() != 5 {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }

            let key = header[1];
            if key != key {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }
            let flags = header[2].parse()?;
            let length = header[3].parse()?;

            let mut buffer = vec![0; length];
            self.reader.read_exact(buffer.as_mut_slice())?;

            result.insert(key.to_string(), FromMemcacheValue::from_memcache_value(buffer, flags)?);

            // read the rest \r\n
            let mut s = String::new();
            let _ = self.reader.read_line(&mut s)?;
            if s != "\r\n" {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }
        }

        return Ok(result);
    }

    pub(super) fn set<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
        expiration: u32,
    ) -> Result<(), MemcacheError> {
        let options = Options{exptime: expiration, ..Default::default()};
        return self.store(StoreCommand::Set, key, value, &options);
    }

    pub(super) fn add<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
        expiration: u32,
    ) -> Result<(), MemcacheError> {
        let options = Options{exptime: expiration, ..Default::default()};
        return self.store(StoreCommand::Add, key, value, &options);
    }

    pub(super) fn replace<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
        expiration: u32,
    ) -> Result<(), MemcacheError> {
        let options = Options{exptime: expiration, ..Default::default()};
        return self.store(StoreCommand::Replace, key, value, &options);
    }

    pub(super) fn append<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
    ) -> Result<(), MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        return self.store(StoreCommand::Append, key, value, &Default::default());
    }

    pub(super) fn prepend<V: ToMemcacheValue<Stream>>(
        &mut self,
        key: &str,
        value: V,
    ) -> Result<(), MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        return self.store(StoreCommand::Prepend, key, value, &Default::default());
    }

    pub(super) fn delete(&mut self, key: &str) -> Result<bool, MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        write!(self.reader.get_mut(), "delete {}\r\n", key)?;
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "DELETED\r\n" {
            return Ok(true);
        } else if s == "NOT_FOUND\r\n" {
            return Ok(false);
        } else {
            return Err(MemcacheError::ClientError(String::from("invalid server response")));
        }
    }

    pub(super) fn increment(&mut self, key: &str, amount: u64) -> Result<u64, MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        write!(self.reader.get_mut(), "incr {} {}\r\n", key, amount)?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "NOT_FOUND\r\n" {
            return Err(MemcacheError::from(1));
        } else {
            match s.trim_end_matches("\r\n").parse::<u64>() {
                Ok(n) => return Ok(n),
                Err(_) => return Err(MemcacheError::ClientError("invalid server response".into())),
            }
        }
    }

    pub(super) fn decrement(&mut self, key: &str, amount: u64) -> Result<u64, MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        write!(self.reader.get_mut(), "decr {} {}\r\n", key, amount)?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "NOT_FOUND\r\n" {
            return Err(MemcacheError::from(1));
        } else {
            match s.trim_end_matches("\r\n").parse::<u64>() {
                Ok(n) => return Ok(n),
                Err(_) => return Err(MemcacheError::ClientError("invalid server response".into())),
            }
        }
    }

    pub(super) fn touch(&mut self, key: &str, expiration: u32) -> Result<bool, MemcacheError> {
        if key.len() > 250 {
            return Err(MemcacheError::ClientError(String::from("key is too long")));
        }
        write!(self.reader.get_mut(), "touch {} {}\r\n", key, expiration)?;
        self.reader.get_mut().flush()?;
        let mut s = String::new();
        let _ = self.reader.read_line(&mut s);
        if is_memcache_error(s.as_str()) {
            return Err(MemcacheError::from(s));
        } else if s == "TOUCHED\r\n" {
            return Ok(true);
        } else if s == "NOT_FOUND\r\n" {
            return Ok(false);
        } else {
            return Err(MemcacheError::ClientError(String::from("invalid server response")));
        }
    }

    pub(super) fn stats(&mut self) -> Result<Stats, MemcacheError> {
        self.reader.get_mut().write(b"stats\r\n")?;
        self.reader.get_mut().flush()?;

        let mut result: Stats = HashMap::new();
        loop {
            let mut s = String::new();
            let _ = self.reader.read_line(&mut s)?;

            if is_memcache_error(s.as_str()) {
                return Err(MemcacheError::from(s));
            } else if s.starts_with("END") {
                break;
            } else if !s.starts_with("STAT") {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }

            let stat: Vec<_> = s.trim_end_matches("\r\n").split(" ").collect();
            if stat.len() < 3 {
                return Err(MemcacheError::ClientError("invalid server response".into()));
            }
            let key = stat[1];
            let value = s.trim_start_matches(format!("STAT {}", key).as_str());
            result.insert(key.into(), value.into());
        }

        return Ok(result);
    }
}

fn is_memcache_error(s: &str) -> bool {
    return s == "ERROR\r\n" || s.starts_with("CIENT_ERROR") || s.starts_with("SERVER_ERROR");
}
<|endoftext|>


<commit_before>use app_units::Au;
use device::{TextureFilter, TextureId};
use euclid::Size2D;
use fnv::FnvHasher;
use freelist::FreeList;
use internal_types::{FontTemplate, GlyphKey, RasterItem};
use internal_types::{TextureUpdateList, DrawListId, DrawList};
use platform::font::{FontContext, RasterizedGlyph};
use renderer::BLUR_INFLATION_FACTOR;
use resource_list::ResourceList;
use scoped_threadpool;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_state::DefaultState;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
use std::sync::atomic::Ordering::SeqCst;
use std::thread;
use std::time::Duration;
use texture_cache::{TextureCache, TextureCacheItem, TextureCacheItemId};
use texture_cache::{BorderType, TextureInsertOp};
use webrender_traits::{Epoch, FontKey, ImageKey, ImageFormat, DisplayItem, ImageRendering};
use webrender_traits::{WebGLContextId};

static FONT_CONTEXT_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;

thread_local!(pub static FONT_CONTEXT: RefCell<FontContext> = RefCell::new(FontContext::new()));

struct ImageResource {
    bytes: Vec<u8>,
    width: u32,
    height: u32,
    format: ImageFormat,
    epoch: Epoch,
}

struct GlyphRasterJob {
    glyph_key: GlyphKey,
    result: Option<RasterizedGlyph>,
}

struct CachedImageInfo {
    texture_cache_id: TextureCacheItemId,
    epoch: Epoch,
}

pub struct ResourceCache {
    cached_glyphs: HashMap<GlyphKey, Option<TextureCacheItemId>, DefaultState<FnvHasher>>,
    cached_rasters: HashMap<RasterItem, TextureCacheItemId, DefaultState<FnvHasher>>,
    cached_images: HashMap<(ImageKey, ImageRendering), CachedImageInfo, DefaultState<FnvHasher>>,
    webgl_textures: HashMap<WebGLContextId, TextureId, DefaultState<FnvHasher>>,

    draw_lists: FreeList<DrawList>,
    font_templates: HashMap<FontKey, FontTemplate, DefaultState<FnvHasher>>,
    image_templates: HashMap<ImageKey, ImageResource, DefaultState<FnvHasher>>,
    device_pixel_ratio: f32,
    enable_aa: bool,

    texture_cache: TextureCache,

    pending_raster_jobs: Vec<GlyphRasterJob>,

    white_image_id: TextureCacheItemId,
    dummy_mask_image_id: TextureCacheItemId,
}

impl ResourceCache {
    pub fn new(thread_pool: &mut scoped_threadpool::Pool,
               texture_cache: TextureCache,
               white_image_id: TextureCacheItemId,
               dummy_mask_image_id: TextureCacheItemId,
               device_pixel_ratio: f32,
               enable_aa: bool) -> ResourceCache {

        let thread_count = thread_pool.thread_count() as usize;
        thread_pool.scoped(|scope| {
            for _ in 0..thread_count {
                scope.execute(|| {
                    FONT_CONTEXT.with(|_| {
                        FONT_CONTEXT_COUNT.fetch_add(1, SeqCst);
                        while FONT_CONTEXT_COUNT.load(SeqCst) != thread_count {
                            thread::sleep(Duration::from_millis(1));
                        }
                    });
                });
            }
        });

        ResourceCache {
            cached_glyphs: HashMap::with_hash_state(Default::default()),
            cached_rasters: HashMap::with_hash_state(Default::default()),
            cached_images: HashMap::with_hash_state(Default::default()),
            webgl_textures: HashMap::with_hash_state(Default::default()),
            draw_lists: FreeList::new(),
            font_templates: HashMap::with_hash_state(Default::default()),
            image_templates: HashMap::with_hash_state(Default::default()),
            texture_cache: texture_cache,
            pending_raster_jobs: Vec::new(),
            device_pixel_ratio: device_pixel_ratio,
            white_image_id: white_image_id,
            dummy_mask_image_id: dummy_mask_image_id,
            enable_aa: enable_aa,
        }
    }

    pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) {
        self.font_templates.insert(font_key, template);
    }

    pub fn add_image_template(&mut self,
                              image_key: ImageKey,
                              width: u32,
                              height: u32,
                              format: ImageFormat,
                              bytes: Vec<u8>) {
        let resource = ImageResource {
            width: width,
            height: height,
            format: format,
            bytes: bytes,
            epoch: Epoch(0),
        };

        self.image_templates.insert(image_key, resource);
    }

    pub fn update_image_template(&mut self,
                                 image_key: ImageKey,
                                 width: u32,
                                 height: u32,
                                 format: ImageFormat,
                                 bytes: Vec<u8>) {
        let next_epoch = match self.image_templates.get(&image_key) {
            Some(image) => {
                let Epoch(current_epoch) = image.epoch;
                Epoch(current_epoch + 1)
            }
            None => {
                Epoch(0)
            }
        };

        let resource = ImageResource {
            width: width,
            height: height,
            format: format,
            bytes: bytes,
            epoch: next_epoch,
        };

        self.image_templates.insert(image_key, resource);
    }

    pub fn add_webgl_texture(&mut self, id: WebGLContextId, texture_id: TextureId, size: Size2D<i32>) {
        self.webgl_textures.insert(id, texture_id);
        self.texture_cache.add_raw_update(texture_id, size);
    }

    pub fn add_resource_list(&mut self, resource_list: &ResourceList) {
        // Update texture cache with any GPU generated procedural items.
        resource_list.for_each_raster(|raster_item| {
            if !self.cached_rasters.contains_key(raster_item) {
                let image_id = self.texture_cache.new_item_id();
                self.texture_cache.insert_raster_op(image_id,
                                                    raster_item,
                                                    self.device_pixel_ratio);
                self.cached_rasters.insert(raster_item.clone(), image_id);
            }
        });

        // Update texture cache with any images that aren't yet uploaded to GPU.
        resource_list.for_each_image(|image_key, image_rendering| {
            let cached_images = &mut self.cached_images;
            let image_template = &self.image_templates[&image_key];

            match cached_images.entry((image_key, image_rendering)) {
                Occupied(entry) => {
                    if entry.get().epoch != image_template.epoch {
                        let image_id = entry.get().texture_cache_id;

                        // TODO: Can we avoid the clone of the bytes here?
                        self.texture_cache.update(image_id,
                                                  image_template.width,
                                                  image_template.height,
                                                  image_template.format,
                                                  image_template.bytes.clone());

                        // Update the cached epoch
                        *entry.into_mut() = CachedImageInfo {
                            texture_cache_id: image_id,
                            epoch: image_template.epoch,
                        };
                    }
                }
                Vacant(entry) => {
                    let image_id = self.texture_cache.new_item_id();

                    let filter = match image_rendering {
                        ImageRendering::Pixelated => TextureFilter::Nearest,
                        ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
                    };

                    // TODO: Can we avoid the clone of the bytes here?
                    self.texture_cache.insert(image_id,
                                              0,
                                              0,
                                              image_template.width,
                                              image_template.height,
                                              image_template.format,
                                              filter,
                                              TextureInsertOp::Blit(image_template.bytes.clone()),
                                              BorderType::SinglePixel);

                    entry.insert(CachedImageInfo {
                        texture_cache_id: image_id,
                        epoch: image_template.epoch,
                    });
                }
            };
        });

        // Update texture cache with any newly rasterized glyphs.
        resource_list.for_each_glyph(|glyph_key| {
            if !self.cached_glyphs.contains_key(glyph_key) {
                self.pending_raster_jobs.push(GlyphRasterJob {
                    glyph_key: glyph_key.clone(),
                    result: None,
                });
            }
        });
    }

    pub fn raster_pending_glyphs(&mut self,
                                 thread_pool: &mut scoped_threadpool::Pool) {
        // Run raster jobs in parallel
        run_raster_jobs(thread_pool,
                        &mut self.pending_raster_jobs,
                        &self.font_templates,
                        self.device_pixel_ratio,
                        self.enable_aa);

        // Add completed raster jobs to the texture cache
        for job in self.pending_raster_jobs.drain(..) {
            let result = job.result.expect("Failed to rasterize the glyph?");
            let image_id = if result.width > 0 && result.height > 0 {
                let image_id = self.texture_cache.new_item_id();
                let texture_width;
                let texture_height;
                let insert_op;
                match job.glyph_key.blur_radius {
                    Au(0) => {
                        texture_width = result.width;
                        texture_height = result.height;
                        insert_op = TextureInsertOp::Blit(result.bytes);
                    }
                    blur_radius => {
                        let blur_radius_px = f32::ceil(blur_radius.to_f32_px() * self.device_pixel_ratio)
                            as u32;
                        texture_width = result.width + blur_radius_px * BLUR_INFLATION_FACTOR;
                        texture_height = result.height + blur_radius_px * BLUR_INFLATION_FACTOR;
                        insert_op = TextureInsertOp::Blur(result.bytes,
                                                          Size2D::new(result.width, result.height),
                                                          blur_radius);
                    }
                }
                self.texture_cache.insert(image_id,
                                          result.left,
                                          result.top,
                                          texture_width,
                                          texture_height,
                                          ImageFormat::RGBA8,
                                          TextureFilter::Linear,
                                          insert_op,
                                          BorderType::NoBorder);
                Some(image_id)
            } else {
                None
            };
            self.cached_glyphs.insert(job.glyph_key, image_id);
        }
    }

    pub fn add_draw_list(&mut self, items: Vec<DisplayItem>) -> DrawListId {
        self.draw_lists.insert(DrawList::new(items))
    }

    pub fn get_draw_list(&self, draw_list_id: DrawListId) -> &DrawList {
        self.draw_lists.get(draw_list_id)
    }

    pub fn get_draw_list_mut(&mut self, draw_list_id: DrawListId) -> &mut DrawList {
        self.draw_lists.get_mut(draw_list_id)
    }

    pub fn remove_draw_list(&mut self, draw_list_id: DrawListId) {
        self.draw_lists.free(draw_list_id);
    }

    pub fn allocate_render_target(&mut self,
                                  width: u32,
                                  height: u32,
                                  format: ImageFormat)
                                  -> TextureId {
        self.texture_cache.allocate_render_target(width, height, format)
    }

    pub fn free_render_target(&mut self, texture_id: TextureId) {
        self.texture_cache.free_render_target(texture_id)
    }

    pub fn pending_updates(&mut self) -> TextureUpdateList {
        self.texture_cache.pending_updates()
    }

    #[inline]
    pub fn get_dummy_mask_image(&self) -> &TextureCacheItem {
        self.texture_cache.get(self.dummy_mask_image_id)
    }

    #[inline]
    pub fn get_dummy_color_image(&self) -> &TextureCacheItem {
        self.texture_cache.get(self.white_image_id)
    }

    #[inline]
    pub fn get_glyph(&self, glyph_key: &GlyphKey) -> Option<&TextureCacheItem> {
        let image_id = self.cached_glyphs[glyph_key];
        image_id.map(|image_id| self.texture_cache.get(image_id))
    }

    #[inline]
    pub fn get_image(&self,
                     image_key: ImageKey,
                     image_rendering: ImageRendering) -> &TextureCacheItem {
        let image_info = &self.cached_images[&(image_key, image_rendering)];
        self.texture_cache.get(image_info.texture_cache_id)
    }

    #[inline]
    pub fn get_raster(&self, raster_item: &RasterItem) -> &TextureCacheItem {
        let image_id = self.cached_rasters[raster_item];
        self.texture_cache.get(image_id)
    }

    #[inline]
    pub fn get_webgl_texture(&self,
                             context_id: &WebGLContextId) -> TextureId {
        self.webgl_textures.get(context_id).unwrap().clone()
    }

    pub fn device_pixel_ratio(&self) -> f32 {
        self.device_pixel_ratio
    }
}

fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool,
                   pending_raster_jobs: &mut Vec<GlyphRasterJob>,
                   font_templates: &HashMap<FontKey, FontTemplate, DefaultState<FnvHasher>>,
                   device_pixel_ratio: f32,
                   enable_aa: bool) {
    // Run raster jobs in parallel
    thread_pool.scoped(|scope| {
        for job in pending_raster_jobs {
            scope.execute(|| {
                let font_template = &font_templates[&job.glyph_key.font_key];
                FONT_CONTEXT.with(move |font_context| {
                    let mut font_context = font_context.borrow_mut();
                    match *font_template {
                        FontTemplate::Raw(ref bytes) => {
                            font_context.add_raw_font(&job.glyph_key.font_key, &**bytes);
                        }
                        FontTemplate::Native(ref native_font_handle) => {
                            font_context.add_native_font(&job.glyph_key.font_key,
                                                         (*native_font_handle).clone());
                        }
                    }
                    job.result = font_context.get_glyph(job.glyph_key.font_key,
                                                        job.glyph_key.size,
                                                        job.glyph_key.index,
                                                        device_pixel_ratio,
                                                        enable_aa);
                });
            });
        }
    });
}
<commit_msg>Don't spin up a thread pool for raster jobs if there aren't any.<commit_after>use app_units::Au;
use device::{TextureFilter, TextureId};
use euclid::Size2D;
use fnv::FnvHasher;
use freelist::FreeList;
use internal_types::{FontTemplate, GlyphKey, RasterItem};
use internal_types::{TextureUpdateList, DrawListId, DrawList};
use platform::font::{FontContext, RasterizedGlyph};
use renderer::BLUR_INFLATION_FACTOR;
use resource_list::ResourceList;
use scoped_threadpool;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_state::DefaultState;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
use std::sync::atomic::Ordering::SeqCst;
use std::thread;
use std::time::Duration;
use texture_cache::{TextureCache, TextureCacheItem, TextureCacheItemId};
use texture_cache::{BorderType, TextureInsertOp};
use webrender_traits::{Epoch, FontKey, ImageKey, ImageFormat, DisplayItem, ImageRendering};
use webrender_traits::{WebGLContextId};

static FONT_CONTEXT_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;

thread_local!(pub static FONT_CONTEXT: RefCell<FontContext> = RefCell::new(FontContext::new()));

struct ImageResource {
    bytes: Vec<u8>,
    width: u32,
    height: u32,
    format: ImageFormat,
    epoch: Epoch,
}

struct GlyphRasterJob {
    glyph_key: GlyphKey,
    result: Option<RasterizedGlyph>,
}

struct CachedImageInfo {
    texture_cache_id: TextureCacheItemId,
    epoch: Epoch,
}

pub struct ResourceCache {
    cached_glyphs: HashMap<GlyphKey, Option<TextureCacheItemId>, DefaultState<FnvHasher>>,
    cached_rasters: HashMap<RasterItem, TextureCacheItemId, DefaultState<FnvHasher>>,
    cached_images: HashMap<(ImageKey, ImageRendering), CachedImageInfo, DefaultState<FnvHasher>>,
    webgl_textures: HashMap<WebGLContextId, TextureId, DefaultState<FnvHasher>>,

    draw_lists: FreeList<DrawList>,
    font_templates: HashMap<FontKey, FontTemplate, DefaultState<FnvHasher>>,
    image_templates: HashMap<ImageKey, ImageResource, DefaultState<FnvHasher>>,
    device_pixel_ratio: f32,
    enable_aa: bool,

    texture_cache: TextureCache,

    pending_raster_jobs: Vec<GlyphRasterJob>,

    white_image_id: TextureCacheItemId,
    dummy_mask_image_id: TextureCacheItemId,
}

impl ResourceCache {
    pub fn new(thread_pool: &mut scoped_threadpool::Pool,
               texture_cache: TextureCache,
               white_image_id: TextureCacheItemId,
               dummy_mask_image_id: TextureCacheItemId,
               device_pixel_ratio: f32,
               enable_aa: bool) -> ResourceCache {

        let thread_count = thread_pool.thread_count() as usize;
        thread_pool.scoped(|scope| {
            for _ in 0..thread_count {
                scope.execute(|| {
                    FONT_CONTEXT.with(|_| {
                        FONT_CONTEXT_COUNT.fetch_add(1, SeqCst);
                        while FONT_CONTEXT_COUNT.load(SeqCst) != thread_count {
                            thread::sleep(Duration::from_millis(1));
                        }
                    });
                });
            }
        });

        ResourceCache {
            cached_glyphs: HashMap::with_hash_state(Default::default()),
            cached_rasters: HashMap::with_hash_state(Default::default()),
            cached_images: HashMap::with_hash_state(Default::default()),
            webgl_textures: HashMap::with_hash_state(Default::default()),
            draw_lists: FreeList::new(),
            font_templates: HashMap::with_hash_state(Default::default()),
            image_templates: HashMap::with_hash_state(Default::default()),
            texture_cache: texture_cache,
            pending_raster_jobs: Vec::new(),
            device_pixel_ratio: device_pixel_ratio,
            white_image_id: white_image_id,
            dummy_mask_image_id: dummy_mask_image_id,
            enable_aa: enable_aa,
        }
    }

    pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) {
        self.font_templates.insert(font_key, template);
    }

    pub fn add_image_template(&mut self,
                              image_key: ImageKey,
                              width: u32,
                              height: u32,
                              format: ImageFormat,
                              bytes: Vec<u8>) {
        let resource = ImageResource {
            width: width,
            height: height,
            format: format,
            bytes: bytes,
            epoch: Epoch(0),
        };

        self.image_templates.insert(image_key, resource);
    }

    pub fn update_image_template(&mut self,
                                 image_key: ImageKey,
                                 width: u32,
                                 height: u32,
                                 format: ImageFormat,
                                 bytes: Vec<u8>) {
        let next_epoch = match self.image_templates.get(&image_key) {
            Some(image) => {
                let Epoch(current_epoch) = image.epoch;
                Epoch(current_epoch + 1)
            }
            None => {
                Epoch(0)
            }
        };

        let resource = ImageResource {
            width: width,
            height: height,
            format: format,
            bytes: bytes,
            epoch: next_epoch,
        };

        self.image_templates.insert(image_key, resource);
    }

    pub fn add_webgl_texture(&mut self, id: WebGLContextId, texture_id: TextureId, size: Size2D<i32>) {
        self.webgl_textures.insert(id, texture_id);
        self.texture_cache.add_raw_update(texture_id, size);
    }

    pub fn add_resource_list(&mut self, resource_list: &ResourceList) {
        // Update texture cache with any GPU generated procedural items.
        resource_list.for_each_raster(|raster_item| {
            if !self.cached_rasters.contains_key(raster_item) {
                let image_id = self.texture_cache.new_item_id();
                self.texture_cache.insert_raster_op(image_id,
                                                    raster_item,
                                                    self.device_pixel_ratio);
                self.cached_rasters.insert(raster_item.clone(), image_id);
            }
        });

        // Update texture cache with any images that aren't yet uploaded to GPU.
        resource_list.for_each_image(|image_key, image_rendering| {
            let cached_images = &mut self.cached_images;
            let image_template = &self.image_templates[&image_key];

            match cached_images.entry((image_key, image_rendering)) {
                Occupied(entry) => {
                    if entry.get().epoch != image_template.epoch {
                        let image_id = entry.get().texture_cache_id;

                        // TODO: Can we avoid the clone of the bytes here?
                        self.texture_cache.update(image_id,
                                                  image_template.width,
                                                  image_template.height,
                                                  image_template.format,
                                                  image_template.bytes.clone());

                        // Update the cached epoch
                        *entry.into_mut() = CachedImageInfo {
                            texture_cache_id: image_id,
                            epoch: image_template.epoch,
                        };
                    }
                }
                Vacant(entry) => {
                    let image_id = self.texture_cache.new_item_id();

                    let filter = match image_rendering {
                        ImageRendering::Pixelated => TextureFilter::Nearest,
                        ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
                    };

                    // TODO: Can we avoid the clone of the bytes here?
                    self.texture_cache.insert(image_id,
                                              0,
                                              0,
                                              image_template.width,
                                              image_template.height,
                                              image_template.format,
                                              filter,
                                              TextureInsertOp::Blit(image_template.bytes.clone()),
                                              BorderType::SinglePixel);

                    entry.insert(CachedImageInfo {
                        texture_cache_id: image_id,
                        epoch: image_template.epoch,
                    });
                }
            };
        });

        // Update texture cache with any newly rasterized glyphs.
        resource_list.for_each_glyph(|glyph_key| {
            if !self.cached_glyphs.contains_key(glyph_key) {
                self.pending_raster_jobs.push(GlyphRasterJob {
                    glyph_key: glyph_key.clone(),
                    result: None,
                });
            }
        });
    }

    pub fn raster_pending_glyphs(&mut self,
                                 thread_pool: &mut scoped_threadpool::Pool) {
        // Run raster jobs in parallel
        run_raster_jobs(thread_pool,
                        &mut self.pending_raster_jobs,
                        &self.font_templates,
                        self.device_pixel_ratio,
                        self.enable_aa);

        // Add completed raster jobs to the texture cache
        for job in self.pending_raster_jobs.drain(..) {
            let result = job.result.expect("Failed to rasterize the glyph?");
            let image_id = if result.width > 0 && result.height > 0 {
                let image_id = self.texture_cache.new_item_id();
                let texture_width;
                let texture_height;
                let insert_op;
                match job.glyph_key.blur_radius {
                    Au(0) => {
                        texture_width = result.width;
                        texture_height = result.height;
                        insert_op = TextureInsertOp::Blit(result.bytes);
                    }
                    blur_radius => {
                        let blur_radius_px = f32::ceil(blur_radius.to_f32_px() * self.device_pixel_ratio)
                            as u32;
                        texture_width = result.width + blur_radius_px * BLUR_INFLATION_FACTOR;
                        texture_height = result.height + blur_radius_px * BLUR_INFLATION_FACTOR;
                        insert_op = TextureInsertOp::Blur(result.bytes,
                                                          Size2D::new(result.width, result.height),
                                                          blur_radius);
                    }
                }
                self.texture_cache.insert(image_id,
                                          result.left,
                                          result.top,
                                          texture_width,
                                          texture_height,
                                          ImageFormat::RGBA8,
                                          TextureFilter::Linear,
                                          insert_op,
                                          BorderType::NoBorder);
                Some(image_id)
            } else {
                None
            };
            self.cached_glyphs.insert(job.glyph_key, image_id);
        }
    }

    pub fn add_draw_list(&mut self, items: Vec<DisplayItem>) -> DrawListId {
        self.draw_lists.insert(DrawList::new(items))
    }

    pub fn get_draw_list(&self, draw_list_id: DrawListId) -> &DrawList {
        self.draw_lists.get(draw_list_id)
    }

    pub fn get_draw_list_mut(&mut self, draw_list_id: DrawListId) -> &mut DrawList {
        self.draw_lists.get_mut(draw_list_id)
    }

    pub fn remove_draw_list(&mut self, draw_list_id: DrawListId) {
        self.draw_lists.free(draw_list_id);
    }

    pub fn allocate_render_target(&mut self,
                                  width: u32,
                                  height: u32,
                                  format: ImageFormat)
                                  -> TextureId {
        self.texture_cache.allocate_render_target(width, height, format)
    }

    pub fn free_render_target(&mut self, texture_id: TextureId) {
        self.texture_cache.free_render_target(texture_id)
    }

    pub fn pending_updates(&mut self) -> TextureUpdateList {
        self.texture_cache.pending_updates()
    }

    #[inline]
    pub fn get_dummy_mask_image(&self) -> &TextureCacheItem {
        self.texture_cache.get(self.dummy_mask_image_id)
    }

    #[inline]
    pub fn get_dummy_color_image(&self) -> &TextureCacheItem {
        self.texture_cache.get(self.white_image_id)
    }

    #[inline]
    pub fn get_glyph(&self, glyph_key: &GlyphKey) -> Option<&TextureCacheItem> {
        let image_id = self.cached_glyphs[glyph_key];
        image_id.map(|image_id| self.texture_cache.get(image_id))
    }

    #[inline]
    pub fn get_image(&self,
                     image_key: ImageKey,
                     image_rendering: ImageRendering) -> &TextureCacheItem {
        let image_info = &self.cached_images[&(image_key, image_rendering)];
        self.texture_cache.get(image_info.texture_cache_id)
    }

    #[inline]
    pub fn get_raster(&self, raster_item: &RasterItem) -> &TextureCacheItem {
        let image_id = self.cached_rasters[raster_item];
        self.texture_cache.get(image_id)
    }

    #[inline]
    pub fn get_webgl_texture(&self,
                             context_id: &WebGLContextId) -> TextureId {
        self.webgl_textures.get(context_id).unwrap().clone()
    }

    pub fn device_pixel_ratio(&self) -> f32 {
        self.device_pixel_ratio
    }
}

fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool,
                   pending_raster_jobs: &mut Vec<GlyphRasterJob>,
                   font_templates: &HashMap<FontKey, FontTemplate, DefaultState<FnvHasher>>,
                   device_pixel_ratio: f32,
                   enable_aa: bool) {
    if pending_raster_jobs.is_empty() {
        return
    }

    // Run raster jobs in parallel
    thread_pool.scoped(|scope| {
        for job in pending_raster_jobs {
            scope.execute(|| {
                let font_template = &font_templates[&job.glyph_key.font_key];
                FONT_CONTEXT.with(move |font_context| {
                    let mut font_context = font_context.borrow_mut();
                    match *font_template {
                        FontTemplate::Raw(ref bytes) => {
                            font_context.add_raw_font(&job.glyph_key.font_key, &**bytes);
                        }
                        FontTemplate::Native(ref native_font_handle) => {
                            font_context.add_native_font(&job.glyph_key.font_key,
                                                         (*native_font_handle).clone());
                        }
                    }
                    job.result = font_context.get_glyph(job.glyph_key.font_key,
                                                        job.glyph_key.size,
                                                        job.glyph_key.index,
                                                        device_pixel_ratio,
                                                        enable_aa);
                });
            });
        }
    });
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::io::Cursor;
use std::ops::Deref;
use std::rc::Rc;

use digest::Digest;
use serde_json;
use sha2::Sha256;
use url::Url;
use url_serde;

use resman::ResMan;
use super::routegraph::RouteGraph;
use super::adjlist::AdjList;

#[derive(Debug)]
pub enum Error {
    /// No effect matches the metadata requested.
    NoMatchingEffect(EffectId),
}

/// Alias for a `Result` with our error type.
pub type ResultE<T> = Result<T, Error>;

/// Serializable info needed to look up an effect.
#[derive(Clone, Debug)]
#[derive(Serialize, Deserialize)]
pub struct EffectId {
    /// Canonical name of the effect
    name: String,
    /// Hash of the effect's definition file, or None if the effect is primitive
    sha256: Option<[u8; 32]>,
    // TODO: consider a smallset.
    // TODO: use #[serde(with = "url_serde")]
    /// List of URLs where the Effect can be obtained
    urls: HashSet<url_serde::Serde<Url>>,
}

/// All information that will be loaded from disk/network to describe how to
/// synthesize the Effect.
#[derive(Debug)]
#[derive(Serialize, Deserialize)]
pub struct EffectDesc {
    meta: EffectMeta,
    adjlist: AdjList,
}

/// Validated version of `EffectDesc`. Guaranteed to be synthesizable.
#[derive(Debug)]
pub struct Effect {
    id: EffectId,
    meta: EffectMeta,
    // TODO: Effects are immutable, so we can make this an AdjList and store
    // the connectivity information separately.
    // option, because effect MAY be primitive.
    data: EffectData,
}

#[derive(Clone, Debug)]
#[derive(Serialize, Deserialize)]
pub struct EffectMeta {
    id: EffectId,
    inputs: Vec<EffectInput>,
    outputs: Vec<EffectOutput>,
}

#[derive(Clone, Debug)]
#[derive(Serialize, Deserialize)]
pub struct EffectIO {
    name: String,
    channel: u8,
}
pub type EffectInput = EffectIO;
pub type EffectOutput = EffectIO;

/// Implementation details of an Effect.
/// Either its route graph, a primitive effect, etc.
#[derive(Debug)]
pub enum EffectData {
    RouteGraph(RouteGraph),
    Primitive(PrimitiveEffect),
}

/// Effects that cannot be decomposed; they have no implementation details and
/// must be implemented directly by the renderer.
#[derive(Debug, Copy, Clone)]
pub enum PrimitiveEffect {
    /// Primitive Delay effect
    Delay,
    /// Primitive Constant effect.
    /// Also serves as a unit step;
    /// Returns the float value for t >= 0, else 0.
    F32Constant,
    /// Sum 2 floating point streams.
    Sum2,
    /// Primitive effect to multiply TWO input streams sample-wise.
    Multiply,
    /// Primitive effect to calculate A/B.
    /// Note: because not all floating point numbers have inverses,
    /// A * (1/B) != A/B. Hence, we need division (not inversion) for proper
    /// precision.
    Divide,
    /// Primitive effect to calculate A%B (true modulo; not remainder.
    /// Result, y,  is always positive: y is bounded by [0, B).
    Modulo,
    /// Primitive effect to return the sample-wise minimum of two input streams.
    /// Max(A, B) can be implemented as -Min(-A, -B).
    /// The choice to define Min instead of Max was mostly arbitrary,
    /// and chosen because Min is more common in linear programming to avoid dealing
    /// with Inf.
    Minimum,
}

impl Effect {
    pub fn are_slots_connected(&self, from_slot: u32, to_slot: u32) -> bool {
        match self.data {
            EffectData::RouteGraph(ref g) => g.are_slots_connected(from_slot, to_slot),
            // For primitive effects, we assume ALL slots are connected.
            _ => true,
        }
    }
    pub fn id(&self) -> &EffectId {
        &self.id
    }
    pub fn meta(&self) -> &EffectMeta {
        &self.meta
    }
    /// Given the effect's information, and an interface by which to load
    /// resources, return an actual Effect.
    pub fn from_id(id: EffectId, resman: &ResMan) -> ResultE<Rc<Self>> {
        // For primitive effects, don't attempt to locate their descriptions (they don't exist)
        let prim_effect = id.get_primitive_url().and_then(|url| match url.path() {
            "/Delay"       => Some(PrimitiveEffect::Delay),
            "/F32Constant" => Some(PrimitiveEffect::F32Constant),
            "/Sum2"        => Some(PrimitiveEffect::Sum2),
            "/Multiply"    => Some(PrimitiveEffect::Multiply),
            "/Divide"      => Some(PrimitiveEffect::Divide),
            "/Modulo"      => Some(PrimitiveEffect::Modulo),
            "/Minimum"     => Some(PrimitiveEffect::Minimum),
            _ => {
                warn!("Unrecognized primitive effect: {} (full url: {})", url.path(), url);
                None
            }
        });
        // Attempt to instantiate a primitive effect, if the URL matched.
        if let Some(prim_effect) = prim_effect {
            if id.sha256 == None {
                let me = Self {
                    meta: EffectMeta {
                        id: id.clone(),
                        // Primitive effects have undocumented I/O;
                        inputs: Default::default(),
                        outputs: Default::default(),
                    },
                    // sha256 was already verified; no need to update it
                    id: id,
                    data: EffectData::Primitive(prim_effect),
                };
                return Ok(Rc::new(me));
            } else {
                warn!("Attempted to load a primitive Effect, but cannot because of mismatched sha256: {:?}", id.sha256);
            }
        }

        // Locate descriptions for non-primitive effects
        for (path, reader) in resman.find_effect(&id) {
            // Try to deserialize to an effect description
            let desc: Result<EffectDesc, serde_json::Error> = serde_json::from_reader(reader);
            match desc {
                Ok(desc) => {
                    if desc.id().name() == id.name() {
                        // TODO: since we've matched the effect, we only need to recalculate
                        // the id if the original search had missing hashes.
                        let id = desc.id();
                        match RouteGraph::from_adjlist(desc.adjlist, resman) {
                            Ok(graph) => {
                                let me = Self {
                                    id,
                                    meta: desc.meta,
                                    data: EffectData::RouteGraph(graph),
                                };
                                // TODO: implement some form of caching
                                return Ok(Rc::new(me));
                            },
                            Err(error) => warn!("[{:?}] RouteGraph::from_adjlist failed: {:?}", path, error)
                        }
                    } else {
                        trace!("[{:?}] Effect names differ: wanted {:?} got {:?}", path, id.name(), desc.id().name());
                    }
                },
                Err(error) => {
                    warn!("[{:?}] Unable to deserialize EffectDesc: {:?}", path, error)
                }
            }
        }
        // No matching effects
        Err(Error::NoMatchingEffect(id))
    }
    pub fn data(&self) -> &EffectData {
        &self.data
    }
}

impl EffectId {
    pub fn new<U>(name: String, sha256: Option<[u8; 32]>, urls: U) -> Self
        where U: IntoIterator<Item=Url>
    {
        Self {
            name,
            sha256,
            urls: urls.into_iter().map(url_serde::Serde).collect(),
        }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn sha256(&self) -> &Option<[u8; 32]> {
        &self.sha256
    }
    /// Returns true if the effect cannot be decomposed.
    /// This is determined by the effect providing a SINGLE url, with the primitive:// Schema
    pub fn is_primitive(&self) -> bool {
        self.urls.len() == 1 && self.urls.iter().all(|url| {
            url.scheme() == "primitive"
        })
    }
    pub fn get_primitive_url(&self) -> Option<&Url> {
        if self.is_primitive() {
            self.urls.iter().next().map(|url| url.deref())
        } else {
            None
        }
    }
}

impl EffectDesc {
    pub fn new(meta: EffectMeta, adjlist: AdjList) -> Self {
        Self{ meta, adjlist }
    }
    pub fn meta(&self) -> &EffectMeta {
        &self.meta
    }
    pub fn id(&self) -> EffectId {
        // TODO: calculate sha using a smaller buffer
        let as_vec = serde_json::to_vec(self).unwrap();
        let result = Sha256::digest_reader(&mut Cursor::new(as_vec)).unwrap();
        let mut hash: [u8; 32] = Default::default();
        hash.copy_from_slice(result.as_slice());
        EffectId {
            name: self.meta.id.name.clone(),
            sha256: Some(hash),
            urls: self.meta.id.urls.clone(),
        }
    }
    pub fn adjlist(&self) -> &AdjList {
        &self.adjlist
    }
}

impl EffectMeta {
    pub fn new<U>(name: String, urls: U, inputs: Vec<EffectInput>, outputs: Vec<EffectOutput>) -> Self
        where U: IntoIterator<Item=Url>
    {
        Self {
            id: EffectId::new(name, None, urls),
            inputs,
            outputs,
        }
    }
    pub fn name(&self) -> &str {
        self.id.name()
    }
    /// Deprecated: will be replaced by an iterator which also works for
    /// primitive effects.
    /// TODO: remove.
    pub fn inputs(&self) -> &Vec<EffectInput> {
        &self.inputs
    }
    pub fn outputs(&self) -> &Vec<EffectOutput> {
        &self.outputs
    }
    pub fn inputs_by_name<'a>(&'a self, name: &'a str) -> impl Iterator<Item=&EffectInput> + 'a {
        self.inputs.iter().filter(move |item| item.name() == name)
    }
    pub fn outputs_by_name<'a>(&'a self, name: &'a str) -> impl Iterator<Item=&EffectOutput> + 'a {
        self.outputs.iter().filter(move |item| item.name() == name)
    }
    pub fn is_valid_input(&self, slotno: u32) -> bool {
        (slotno as usize) < self.inputs.len() || {
            match self.id.get_primitive_url().and_then(PrimitiveEffect::from_url) {
                Some(PrimitiveEffect::F32Constant) => false,
                Some(_) => slotno < 2,
                None => false
            }
        }
    }
    pub fn is_valid_output(&self, slotno: u32) -> bool {
        (slotno as usize) < self.outputs.len() || {
            match self.id.get_primitive_url().and_then(PrimitiveEffect::from_url) {
                Some(PrimitiveEffect::F32Constant) => true,
                Some(_) => slotno < 1,
                None => false
            }
        }
    }
}

impl EffectIO {
    pub fn new(name: String, channel: u8) -> Self {
        Self{ name, channel }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn channel(&self) -> u8 {
        self.channel
    }
    pub fn is_source(&self) -> bool {
        self.name == "source"
    }
    pub fn is_result(&self) -> bool {
        self.name == "result"
    }
}

impl PrimitiveEffect {
    fn from_url(url: &Url) -> Option<Self> {
        if url.scheme() == "primitive" {
            match url.path() {
                "/Delay"       => Some(PrimitiveEffect::Delay),
                "/F32Constant" => Some(PrimitiveEffect::F32Constant),
                "/Sum2"        => Some(PrimitiveEffect::Sum2),
                "/Multiply"    => Some(PrimitiveEffect::Multiply),
                "/Divide"      => Some(PrimitiveEffect::Divide),
                "/Modulo"      => Some(PrimitiveEffect::Modulo),
                "/Minimum"     => Some(PrimitiveEffect::Minimum),
                _ => {
                    warn!("Unrecognized primitive effect: {} (full url: {})", url.path(), url);
                    None
                }
            }
        } else {
            None
        }
    }
}

impl PartialEq for EffectMeta {
    // Equality implemented in a way where we can easily check things like
    //   "Is this the same primitive Delay effect this renderer knows how to implement?"
    fn eq(&self, other: &EffectMeta) -> bool {
        self.id.name == other.id.name &&
            self.id.urls == other.id.urls
    }
}
impl Eq for EffectMeta {}

impl PartialEq for Effect {
    fn eq(&self, other: &Effect) -> bool {
        self.meta == other.meta
    }
}
impl Eq for Effect {}

<commit_msg>effect.rs simplifications<commit_after>use std::collections::HashSet;
use std::io::Cursor;
use std::ops::Deref;
use std::rc::Rc;

use digest::Digest;
use serde_json;
use sha2::Sha256;
use url::Url;
use url_serde;

use resman::ResMan;
use super::routegraph::RouteGraph;
use super::adjlist::AdjList;

#[derive(Debug)]
pub enum Error {
    /// No effect matches the metadata requested.
    NoMatchingEffect(EffectId),
}

/// Alias for a `Result` with our error type.
pub type ResultE<T> = Result<T, Error>;

/// Serializable info needed to look up an effect.
#[derive(Clone, Debug)]
#[derive(Serialize, Deserialize)]
pub struct EffectId {
    /// Canonical name of the effect
    name: String,
    /// Hash of the effect's definition file, or None if the effect is primitive
    sha256: Option<[u8; 32]>,
    // TODO: consider a smallset.
    // TODO: use #[serde(with = "url_serde")]
    /// List of URLs where the Effect can be obtained
    urls: HashSet<url_serde::Serde<Url>>,
}

/// All information that will be loaded from disk/network to describe how to
/// synthesize the Effect.
#[derive(Debug)]
#[derive(Serialize, Deserialize)]
pub struct EffectDesc {
    meta: EffectMeta,
    adjlist: AdjList,
}

/// Validated version of `EffectDesc`. Guaranteed to be synthesizable.
#[derive(Debug)]
pub struct Effect {
    meta: EffectMeta,
    // TODO: Effects are immutable, so we can make this an AdjList and store
    // the connectivity information separately.
    data: EffectData,
}

#[derive(Clone, Debug)]
#[derive(Serialize, Deserialize)]
pub struct EffectMeta {
    id: EffectId,
    inputs: Vec<EffectInput>,
    outputs: Vec<EffectOutput>,
}

#[derive(Clone, Debug)]
#[derive(Serialize, Deserialize)]
pub struct EffectIO {
    name: String,
    channel: u8,
}
pub type EffectInput = EffectIO;
pub type EffectOutput = EffectIO;

/// Implementation details of an Effect.
/// Either its route graph, a primitive effect, etc.
#[derive(Debug)]
pub enum EffectData {
    RouteGraph(RouteGraph),
    Primitive(PrimitiveEffect),
}

/// Effects that cannot be decomposed; they have no implementation details and
/// must be implemented directly by the renderer.
#[derive(Debug, Copy, Clone)]
pub enum PrimitiveEffect {
    /// Primitive Delay effect
    Delay,
    /// Primitive Constant effect.
    /// Also serves as a unit step;
    /// Returns the float value for t >= 0, else 0.
    F32Constant,
    /// Sum 2 floating point streams.
    Sum2,
    /// Primitive effect to multiply TWO input streams sample-wise.
    Multiply,
    /// Primitive effect to calculate A/B.
    /// Note: because not all floating point numbers have inverses,
    /// A * (1/B) != A/B. Hence, we need division (not inversion) for proper
    /// precision.
    Divide,
    /// Primitive effect to calculate A%B (true modulo; not remainder.
    /// Result, y,  is always positive: y is bounded by [0, B).
    Modulo,
    /// Primitive effect to return the sample-wise minimum of two input streams.
    /// Max(A, B) can be implemented as -Min(-A, -B).
    /// The choice to define Min instead of Max was mostly arbitrary,
    /// and chosen because Min is more common in linear programming to avoid dealing
    /// with Inf.
    Minimum,
}

impl Effect {
    pub fn are_slots_connected(&self, from_slot: u32, to_slot: u32) -> bool {
        match self.data {
            EffectData::RouteGraph(ref g) => g.are_slots_connected(from_slot, to_slot),
            // For primitive effects, we assume ALL slots are connected.
            _ => true,
        }
    }
    pub fn id(&self) -> &EffectId {
        &self.meta.id
    }
    pub fn meta(&self) -> &EffectMeta {
        &self.meta
    }
    /// Given the effect's information, and an interface by which to load
    /// resources, return an actual Effect.
    pub fn from_id(id: EffectId, resman: &ResMan) -> ResultE<Rc<Self>> {
        // For primitive effects, don't attempt to locate their descriptions (they don't exist)
        let prim_effect = id.get_primitive_url().and_then(PrimitiveEffect::from_url);
        // Attempt to instantiate a primitive effect, if the URL matched.
        if let Some(prim_effect) = prim_effect {
            if id.sha256 == None {
                let me = Self {
                    meta: EffectMeta {
                        // sha256 was already verified; no need to update it
                        id: id,
                        // Primitive effects have undocumented I/O;
                        inputs: Default::default(),
                        outputs: Default::default(),
                    },
                    data: EffectData::Primitive(prim_effect),
                };
                return Ok(Rc::new(me));
            } else {
                warn!("Attempted to load a primitive Effect, but cannot because of mismatched sha256: {:?}", id.sha256);
            }
        }

        // Locate descriptions for non-primitive effects
        for (path, reader) in resman.find_effect(&id) {
            // Try to deserialize to an effect description
            let desc: Result<EffectDesc, serde_json::Error> = serde_json::from_reader(reader);
            match desc {
                Ok(mut desc) => {
                    if desc.meta.id.name() == id.name() {
                        desc.update_id();
                        match RouteGraph::from_adjlist(desc.adjlist, resman) {
                            Ok(graph) => {
                                let me = Self {
                                    meta: desc.meta,
                                    data: EffectData::RouteGraph(graph),
                                };
                                // TODO: implement some form of caching
                                return Ok(Rc::new(me));
                            },
                            Err(error) => warn!("[{:?}] RouteGraph::from_adjlist failed: {:?}", path, error)
                        }
                    } else {
                        trace!("[{:?}] Effect names differ: wanted {:?} got {:?}", path, id.name(), desc.meta.id.name());
                    }
                },
                Err(error) => {
                    warn!("[{:?}] Unable to deserialize EffectDesc: {:?}", path, error)
                }
            }
        }
        // No matching effects
        Err(Error::NoMatchingEffect(id))
    }
    pub fn data(&self) -> &EffectData {
        &self.data
    }
}

impl EffectId {
    pub fn new<U>(name: String, sha256: Option<[u8; 32]>, urls: U) -> Self
        where U: IntoIterator<Item=Url>
    {
        Self {
            name,
            sha256,
            urls: urls.into_iter().map(url_serde::Serde).collect(),
        }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn sha256(&self) -> &Option<[u8; 32]> {
        &self.sha256
    }
    /// Returns true if the effect cannot be decomposed.
    /// This is determined by the effect providing a SINGLE url, with the primitive:// Schema
    pub fn is_primitive(&self) -> bool {
        self.urls.len() == 1 && self.urls.iter().all(|url| {
            url.scheme() == "primitive"
        })
    }
    pub fn get_primitive_url(&self) -> Option<&Url> {
        if self.is_primitive() {
            self.urls.iter().next().map(|url| url.deref())
        } else {
            None
        }
    }
}

impl EffectDesc {
    pub fn new(meta: EffectMeta, adjlist: AdjList) -> Self {
        Self{ meta, adjlist }
    }
    pub fn meta(&self) -> &EffectMeta {
        &self.meta
    }
    /// Make sure the id is fully populated with hashes, etc.
    fn update_id(&mut self) {
        if self.meta.id.sha256.is_none() {
            // TODO: calculate sha using a smaller buffer
            let as_vec = serde_json::to_vec(self).unwrap();
            let result = Sha256::digest_reader(&mut Cursor::new(as_vec)).unwrap();
            let mut hash: [u8; 32] = Default::default();
            hash.copy_from_slice(result.as_slice());
            self.meta.id.sha256 = Some(hash);
        }
    }
}

impl EffectMeta {
    pub fn new<U>(name: String, urls: U, inputs: Vec<EffectInput>, outputs: Vec<EffectOutput>) -> Self
        where U: IntoIterator<Item=Url>
    {
        Self {
            id: EffectId::new(name, None, urls),
            inputs,
            outputs,
        }
    }
    pub fn name(&self) -> &str {
        self.id.name()
    }
    /// Deprecated: will be replaced by an iterator which also works for
    /// primitive effects.
    /// TODO: remove.
    pub fn inputs(&self) -> &Vec<EffectInput> {
        &self.inputs
    }
    pub fn outputs(&self) -> &Vec<EffectOutput> {
        &self.outputs
    }
    pub fn inputs_by_name<'a>(&'a self, name: &'a str) -> impl Iterator<Item=&EffectInput> + 'a {
        self.inputs.iter().filter(move |item| item.name() == name)
    }
    pub fn outputs_by_name<'a>(&'a self, name: &'a str) -> impl Iterator<Item=&EffectOutput> + 'a {
        self.outputs.iter().filter(move |item| item.name() == name)
    }
    pub fn is_valid_input(&self, slotno: u32) -> bool {
        (slotno as usize) < self.inputs.len() || {
            match self.id.get_primitive_url().and_then(PrimitiveEffect::from_url) {
                Some(PrimitiveEffect::F32Constant) => false,
                Some(_) => slotno < 2,
                None => false
            }
        }
    }
    pub fn is_valid_output(&self, slotno: u32) -> bool {
        (slotno as usize) < self.outputs.len() || {
            match self.id.get_primitive_url().and_then(PrimitiveEffect::from_url) {
                Some(PrimitiveEffect::F32Constant) => true,
                Some(_) => slotno < 1,
                None => false
            }
        }
    }
}

impl EffectIO {
    pub fn new(name: String, channel: u8) -> Self {
        Self{ name, channel }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn channel(&self) -> u8 {
        self.channel
    }
    pub fn is_source(&self) -> bool {
        self.name == "source"
    }
    pub fn is_result(&self) -> bool {
        self.name == "result"
    }
}

impl PrimitiveEffect {
    fn from_url(url: &Url) -> Option<Self> {
        if url.scheme() == "primitive" {
            match url.path() {
                "/Delay"       => Some(PrimitiveEffect::Delay),
                "/F32Constant" => Some(PrimitiveEffect::F32Constant),
                "/Sum2"        => Some(PrimitiveEffect::Sum2),
                "/Multiply"    => Some(PrimitiveEffect::Multiply),
                "/Divide"      => Some(PrimitiveEffect::Divide),
                "/Modulo"      => Some(PrimitiveEffect::Modulo),
                "/Minimum"     => Some(PrimitiveEffect::Minimum),
                _ => {
                    warn!("Unrecognized primitive effect: {} (full url: {})", url.path(), url);
                    None
                }
            }
        } else {
            None
        }
    }
}

impl PartialEq for EffectMeta {
    // Equality implemented in a way where we can easily check things like
    //   "Is this the same primitive Delay effect this renderer knows how to implement?"
    fn eq(&self, other: &EffectMeta) -> bool {
        self.id.name == other.id.name &&
            self.id.urls == other.id.urls
    }
}
impl Eq for EffectMeta {}

<|endoftext|>


<commit_before>use time::{SteadyTime, Duration};
use std::collections::BTreeMap;
use std::cmp::Ordering;
use std::fmt;
use std::fmt::Debug;

#[derive(Clone)]
struct Task<Token> where Token: Clone {
    interval: Duration,
    run_offset: SteadyTime,
    token: Token,
    bond: TaskBond
}

#[derive(Clone, Debug)]
enum TaskBond {
    OneOff,
    Perpetual
}

impl<Token> fmt::Debug for Task<Token> where Token: Clone {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.bond {
            TaskBond::Perpetual => write!(f, "Task(every {}ms)", self.interval.num_milliseconds()),
            TaskBond::OneOff => write!(f, "Task(after {}ms)", self.interval.num_milliseconds())
        }
    }
}

impl<Token> Task<Token> where Token: Clone {
    fn new(interval: Duration, run_offset: SteadyTime, bond: TaskBond, token: Token) -> Task<Token> {
        assert!(interval > Duration::seconds(0)); // negative interval would make schedule go back in time!
        Task {
            interval: interval,
            run_offset: run_offset,
            bond: bond,
            token: token
        }
    }

    fn next(self) -> Task<Token> {
        Task {
            run_offset: self.run_offset + self.interval,
            .. self
        }
    }

    fn schedule(&self) -> SteadyTime {
        self.run_offset + self.interval
    }
}

pub trait TimeSource {
    fn now(&self) -> SteadyTime;
    fn wait(&mut self, duration: Duration);
}

// TODO: should that be u64?
type TimePoint = u32;

pub enum SchedulerAction {
    None,
    Wait(Duration),
    Skip(Vec<TimePoint>),
    Yield(TimePoint)
}

pub enum Schedule<Token> {
    Missed(Vec<Token>),
    Current(Vec<Token>)
}

pub struct Scheduler<Token, TS> where TS: TimeSource, Token: Clone {
    offset: SteadyTime,
    time_point_interval: Duration,
    tasks: BTreeMap<TimePoint, Vec<Task<Token>>>,
    time_source: TS
}

impl<Token, TS> Scheduler<Token, TS> where TS: TimeSource, Token: Clone {
    pub fn new(time_point_interval: Duration, time_source: TS) -> Scheduler<Token, TS> where TS: TimeSource {
        Scheduler {
            offset: time_source.now(),
            time_point_interval: time_point_interval,
            tasks: BTreeMap::new(),
            time_source: time_source
        }
    }

    fn schedule(&mut self, task: Task<Token>) {
        let now = self.time_source.now();
        assert!(now > self.offset);

        let task = task.next();
        let time_point = self.to_time_point(task.schedule() - now);

        self.tasks.entry(time_point).or_insert(Vec::new()).push(task);
    }

    pub fn after(&mut self, duration: Duration, token: Token) {
        self.schedule(Task::new(duration, SteadyTime::now(), TaskBond::OneOff, token));
    }

    pub fn every(&mut self, duration: Duration, token: Token) {
        self.schedule(Task::new(duration, SteadyTime::now(), TaskBond::Perpetual, token));
    }

    // TODO: we may miss some time points if wait waits longer
    fn next_action(&self) -> SchedulerAction {
        let now = self.time_source.now();
        let current_time_point = self.to_time_point(now - self.offset);

        match self.tasks.iter().next() {
            None => SchedulerAction::None,
            Some((&time_point, _)) => {
                match time_point.cmp(&current_time_point) {
                    Ordering::Greater => SchedulerAction::Wait((self.offset + self.to_duration(time_point)) - now),
                    //TODO: make task Eq for sorting by time
                    Ordering::Less => SchedulerAction::Skip(self.tasks.iter().take_while(|&(&time_point, &_)| time_point < current_time_point).map(|(time_point, _)| time_point.clone()).collect()),
                    Ordering::Equal => SchedulerAction::Yield(time_point)
                }
            }
        }
    }

    pub fn wait(&mut self) -> Option<Schedule<Token>> {
        match self.next_action() {
            SchedulerAction::None => None,
            SchedulerAction::Wait(duration) => {
                self.time_source.wait(duration);
                self.wait()
            },
            SchedulerAction::Skip(time_points) => {
                let tasks: Vec<Task<Token>> = time_points.iter().flat_map(|time_point|
                        self.tasks.remove(&time_point).unwrap()
                    ).collect();
                for task in tasks.iter() {
                    match task.bond {
                        TaskBond::Perpetual => self.schedule(task.clone()),
                        TaskBond::OneOff => ()
                    };
                }
                Some(Schedule::Missed(tasks.into_iter().map(|task| task.token).collect()))
            },
            SchedulerAction::Yield(time_point) => {
                let mut tasks = self.tasks.remove(&time_point).unwrap();
                for task in tasks.iter() {
                    match task.bond {
                        TaskBond::Perpetual => self.schedule(task.clone()),
                        TaskBond::OneOff => ()
                    };
                }

                tasks.sort_by(|a, b| a.run_offset.cmp(&b.run_offset));

                Some(Schedule::Current(tasks.into_iter().map(|task| task.token).collect()))
            }
        }
    }

    #[allow(dead_code)]
    pub fn time_source(&self) -> &TimeSource {
        &self.time_source
    }

    fn to_time_point(&self, duration: Duration) -> TimePoint {
        let interval = self.time_point_interval.num_microseconds().unwrap();
        let duration = duration.num_microseconds().unwrap();
        assert!(duration >= 0);

        let time_point = duration / interval;
        if duration % interval != 0 {
            time_point + 1; // ceil
        }
        time_point as TimePoint
    }

    fn to_duration(&self, time_point: TimePoint) -> Duration {
        self.time_point_interval * (time_point as i32)
    }
}

#[cfg(test)]
mod task {
    use time::{SteadyTime, Duration};
    use super::{Task, TaskBond};

    #[test]
    fn getting_next_schedule() {
        let now = SteadyTime::now();
        let interval = Duration::seconds(1);
        let task = Task::new(interval, now, TaskBond::OneOff, 42);
        println!("task: {:?}", task);
        assert_eq!(task.schedule(), now + interval);
        assert_eq!(task.next().next().schedule(), now + interval * 3);
    }
}

<commit_msg>it compiles<commit_after>use time::{SteadyTime, Duration};
use std::thread::sleep_ms;
use std::collections::BTreeMap;
use std::cmp::Ordering;
use std::fmt;
use std::fmt::Debug;

#[derive(Clone)]
struct Task<Token> where Token: Clone {
    interval: Duration,
    run_offset: Duration,
    token: Token,
    bond: TaskBond
}

#[derive(Clone, Debug)]
enum TaskBond {
    OneOff,
    Perpetual
}

impl<Token> fmt::Debug for Task<Token> where Token: Clone {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.bond {
            TaskBond::Perpetual => write!(f, "Task(every {}ms)", self.interval.num_milliseconds()),
            TaskBond::OneOff => write!(f, "Task(after {}ms)", self.interval.num_milliseconds())
        }
    }
}

impl<Token> Task<Token> where Token: Clone {
    fn new(interval: Duration, run_offset: Duration, bond: TaskBond, token: Token) -> Task<Token> {
        assert!(interval > Duration::seconds(0)); // negative interval would make schedule go back in time!
        Task {
            interval: interval,
            run_offset: run_offset,
            bond: bond,
            token: token
        }
    }

    //TODO: OneOff task should not have this
    fn next(self) -> Task<Token> {
        Task {
            run_offset: self.run_offset + self.interval,
            .. self
        }
    }

    fn schedule(&self) -> Duration {
        self.run_offset + self.interval
    }
}

pub trait TimeSource {
    // Duration since this TimeSource was crated
    fn now(&self) -> Duration;
    // Blocks for given duration
    fn wait(&mut self, duration: Duration);
}

struct SteadyTimeSource {
    offset: SteadyTime
}

impl SteadyTimeSource {
    fn new() -> SteadyTimeSource {
        SteadyTimeSource {
            offset: SteadyTime::now()
        }
    }
}

impl TimeSource for SteadyTimeSource {
    fn now(&self) -> Duration {
        SteadyTime::now() - self.offset
    }

    fn wait(&mut self, duration: Duration) {
        sleep_ms(duration.num_milliseconds() as u32)
    }
}

// TODO: should that be u64?
type TimePoint = u32;

pub enum SchedulerAction {
    None,
    Wait(Duration),
    Skip(Vec<TimePoint>),
    Yield(TimePoint)
}

pub enum Schedule<Token> {
    Missed(Vec<Token>),
    Current(Vec<Token>)
}

pub struct Scheduler<Token, TS> where TS: TimeSource, Token: Clone {
    time_point_interval: Duration,
    tasks: BTreeMap<TimePoint, Vec<Task<Token>>>,
    time_source: TS
}

impl<Token, TS> Scheduler<Token, TS> where TS: TimeSource, Token: Clone {
    //TODO: way to specify how many durations it is OK to consider task current
    pub fn new(time_point_interval: Duration, time_source: TS) -> Scheduler<Token, TS> where TS: TimeSource {
        assert!(time_point_interval > Duration::seconds(0));
        Scheduler {
            time_point_interval: time_point_interval,
            tasks: BTreeMap::new(),
            time_source: time_source
        }
    }

    //TODO: with_time_source()

    fn schedule(&mut self, task: Task<Token>) {
        let now = self.time_source.now();

        let task = task.next();
        let time_point = self.to_time_point(task.schedule() - now);

        self.tasks.entry(time_point).or_insert(Vec::new()).push(task);
    }

    pub fn after(&mut self, duration: Duration, token: Token) {
        let task = Task::new(duration, self.time_source.now(), TaskBond::OneOff, token);
        self.schedule(task);
    }

    pub fn every(&mut self, duration: Duration, token: Token) {
        let task = Task::new(duration, self.time_source.now(), TaskBond::Perpetual, token);
        self.schedule(task);
    }

    // TODO: we may miss some time points if wait waits longer
    fn next_action(&self) -> SchedulerAction {
        let now = self.time_source.now();
        let current_time_point = self.to_time_point(now);

        match self.tasks.iter().next() {
            None => SchedulerAction::None,
            Some((&time_point, _)) => {
                match time_point.cmp(&current_time_point) {
                    Ordering::Greater => SchedulerAction::Wait((self.to_duration(time_point)) - now),
                    //TODO: make task Eq for sorting by time
                    Ordering::Less => SchedulerAction::Skip(self.tasks.iter().take_while(|&(&time_point, &_)| time_point < current_time_point).map(|(time_point, _)| time_point.clone()).collect()),
                    Ordering::Equal => SchedulerAction::Yield(time_point)
                }
            }
        }
    }

    //TODO: rename to next; don't wait but return NextIn(duration) or something
    pub fn wait(&mut self) -> Option<Schedule<Token>> {
        match self.next_action() {
            SchedulerAction::None => None,
            SchedulerAction::Wait(duration) => {
                self.time_source.wait(duration);
                self.wait()
            },
            SchedulerAction::Skip(time_points) => {
                let tasks: Vec<Task<Token>> = time_points.iter().flat_map(|time_point|
                        self.tasks.remove(&time_point).unwrap()
                    ).collect();
                for task in tasks.iter() {
                    match task.bond {
                        TaskBond::Perpetual => self.schedule(task.clone()),
                        TaskBond::OneOff => ()
                    };
                }
                Some(Schedule::Missed(tasks.into_iter().map(|task| task.token).collect()))
            },
            SchedulerAction::Yield(time_point) => {
                let mut tasks = self.tasks.remove(&time_point).unwrap();
                for task in tasks.iter() {
                    match task.bond {
                        TaskBond::Perpetual => self.schedule(task.clone()),
                        TaskBond::OneOff => ()
                    };
                }

                tasks.sort_by(|a, b| a.run_offset.cmp(&b.run_offset));

                Some(Schedule::Current(tasks.into_iter().map(|task| task.token).collect()))
            }
        }
    }

    #[allow(dead_code)]
    pub fn time_source(&self) -> &TimeSource {
        &self.time_source
    }

    fn to_time_point(&self, duration: Duration) -> TimePoint {
        let interval = self.time_point_interval.num_microseconds().unwrap();
        let duration = duration.num_microseconds().unwrap();
        assert!(duration >= 0);

        let time_point = duration / interval;
        if duration % interval != 0 {
            time_point + 1; // ceil
        }
        time_point as TimePoint
    }

    fn to_duration(&self, time_point: TimePoint) -> Duration {
        self.time_point_interval * (time_point as i32)
    }
}

#[cfg(test)]
mod task {
    use time::Duration;
    use super::{Task, TaskBond, TimeSource, Scheduler};

    struct MockTimeSource {
        current_time: Duration
    }

    impl MockTimeSource {
        fn new() -> MockTimeSource {
            MockTimeSource {
                current_time: Duration::seconds(0)
            }
        }
    }

    impl TimeSource for MockTimeSource {
        fn now(&self) -> Duration {
            self.current_time
        }

        fn wait(&mut self, duration: Duration) {
            self.current_time = self.current_time + duration;
        }
    }

    #[test]
    fn getting_next_schedule() {
        let now = Duration::seconds(0);
        let interval = Duration::seconds(1);
        let task = Task::new(interval, now, TaskBond::OneOff, 42);
        println!("task: {:?}", task);
        assert_eq!(task.schedule(), now + interval);
        assert_eq!(task.next().next().schedule(), now + interval * 3);
    }

    #[test]
    fn scheduler() {
        let mut scheduler = Scheduler::new(Duration::seconds(1), MockTimeSource::new());
        scheduler.after(Duration::seconds(1), 1i32);
    }
}

<|endoftext|>


<commit_before>use {Stream, Poll, Async};

/// Do something with the items of a stream, passing it on.
///
/// This is created by the `Stream::inspect` method.
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Inspect<S, F> where S: Stream {
    stream: S,
    inspect: F,
}

pub fn new<S, F>(stream: S, f: F) -> Inspect<S, F>
    where S: Stream,
          F: FnMut(&S::Item) -> (),
{
    Inspect {
        stream: stream,
        inspect: f,
    }
}

impl<S: Stream, F> Inspect<S, F> {
    /// Acquires a reference to the underlying stream that this combinator is
    /// pulling from.
    pub fn get_ref(&self) -> &S {
        &self.stream
    }

    /// Acquires a mutable reference to the underlying stream that this
    /// combinator is pulling from.
    ///
    /// Note that care must be taken to avoid tampering with the state of the
    /// stream which may otherwise confuse this combinator.
    pub fn get_mut(&mut self) -> &mut S {
        &mut self.stream
    }

    /// Consumes this combinator, returning the underlying stream.
    ///
    /// Note that this may discard intermediate state of this combinator, so
    /// care should be taken to avoid losing resources when this is called.
    pub fn into_inner(self) -> S {
        self.stream
    }
}

impl<S, F> Stream for Inspect<S, F>
    where S: Stream,
          F: FnMut(&S::Item),
{
    type Item = S::Item;
    type Error = S::Error;

    fn poll(&mut self) -> Poll<Option<S::Item>, S::Error> {
        match try_ready!(self.stream.poll()) {
            Some(e) => {
                (self.inspect)(&e);
                Ok(Async::Ready(Some(e)))
            }
            None => Ok(Async::Ready(None)),
        }
    }
}
<commit_msg>Forward Sink through `Inspect` (closes #700)<commit_after>use {Stream, Poll, Async};

/// Do something with the items of a stream, passing it on.
///
/// This is created by the `Stream::inspect` method.
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Inspect<S, F> where S: Stream {
    stream: S,
    inspect: F,
}

pub fn new<S, F>(stream: S, f: F) -> Inspect<S, F>
    where S: Stream,
          F: FnMut(&S::Item) -> (),
{
    Inspect {
        stream: stream,
        inspect: f,
    }
}

impl<S: Stream, F> Inspect<S, F> {
    /// Acquires a reference to the underlying stream that this combinator is
    /// pulling from.
    pub fn get_ref(&self) -> &S {
        &self.stream
    }

    /// Acquires a mutable reference to the underlying stream that this
    /// combinator is pulling from.
    ///
    /// Note that care must be taken to avoid tampering with the state of the
    /// stream which may otherwise confuse this combinator.
    pub fn get_mut(&mut self) -> &mut S {
        &mut self.stream
    }

    /// Consumes this combinator, returning the underlying stream.
    ///
    /// Note that this may discard intermediate state of this combinator, so
    /// care should be taken to avoid losing resources when this is called.
    pub fn into_inner(self) -> S {
        self.stream
    }
}

// Forwarding impl of Sink from the underlying stream
impl<S, F> ::sink::Sink for Inspect<S, F>
    where S: ::sink::Sink + Stream
{
    type SinkItem = S::SinkItem;
    type SinkError = S::SinkError;

    fn start_send(&mut self, item: S::SinkItem) -> ::StartSend<S::SinkItem, S::SinkError> {
        self.stream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), S::SinkError> {
        self.stream.poll_complete()
    }

    fn close(&mut self) -> Poll<(), S::SinkError> {
        self.stream.close()
    }
}

impl<S, F> Stream for Inspect<S, F>
    where S: Stream,
          F: FnMut(&S::Item),
{
    type Item = S::Item;
    type Error = S::Error;

    fn poll(&mut self) -> Poll<Option<S::Item>, S::Error> {
        match try_ready!(self.stream.poll()) {
            Some(e) => {
                (self.inspect)(&e);
                Ok(Async::Ready(Some(e)))
            }
            None => Ok(Async::Ready(None)),
        }
    }
}
<|endoftext|>