<commit_before>use std::collections::HashMap;
use std::fs;
use std::path::*;
use std::io::{Result, Write};
use case::CaseExt;

use env::Env;
use file_saver::*;
use library;
use nameutil::*;
use super::ffi_type::ffi_type;
use super::functions;
use super::statics;
use super::super::general::{self, tabs};
use traits::*;

pub fn generate(env: &Env) {
    println!("generating sys for {}", env.config.library_name);

    let path =  PathBuf::from(&env.config.target_path)
        .join(file_name_sys("lib"));

    let parent = path.parent().unwrap();
    //TODO: do only if not exists
    let _ = fs::create_dir(parent);

    println!("Generating file {:?}", path);
    save_to_file(&path, &mut |w| generate_lib(w, env));
}

fn generate_lib<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    try!(general::start_comments(w, &env.config));
    try!(statics::begin(w));

    try!(generate_extern_crates(w, env));
    try!(statics::after_extern_crates(w));

    if env.config.library_name != "GLib" {
        try!(statics::use_glib_ffi(w));
    } else {
        try!(statics::only_for_glib(w));
    }
    if env.config.library_name == "Gtk" {
        try!(statics::only_for_gtk(w));
    }

    let ns = env.library.namespace(library::MAIN_NAMESPACE);
    let classes = prepare(ns);
    let interfaces = prepare(ns);

    try!(generate_aliases(w, env, &prepare(ns)));
    try!(generate_enums(w, &prepare(ns)));
    try!(generate_bitfields(w, &prepare(ns)));
    try!(generate_unions(w, &prepare(ns)));
    try!(functions::generate_callbacks(w, env, &prepare(ns)));
    try!(generate_records(w, env, &prepare(ns)));
    try!(generate_classes_structs(w, &classes));
    try!(generate_interfaces_structs(w, &interfaces));

    try!(writeln!(w, ""));
    try!(writeln!(w, "extern \"C\" {{"));
    try!(functions::generate_classes_funcs(w, env, &classes));
    try!(functions::generate_interfaces_funcs(w, env, &interfaces));
    try!(functions::generate_other_funcs(w, env, &ns.functions));

    try!(writeln!(w, "\n}}"));

    Ok(())
}

fn generate_extern_crates<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    for library_name in &env.config.external_libraries {
        try!(writeln!(w, "extern crate {0}_sys as {0}_ffi;", crate_name(library_name)));
    }

    Ok(())
}

fn prepare<T: Ord>(ns: &library::Namespace) -> Vec<&T>
where library::Type: MaybeRef<T> {
    let mut vec: Vec<&T> = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(ref x) = typ.maybe_ref() {
            vec.push(x);
        }
    }
    vec.sort();
    vec
}

fn generate_aliases<W: Write>(w: &mut W, env: &Env, items: &[&library::Alias])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        let (comment, c_type) = match ffi_type(env, item.typ, &item.target_c_type) {
            Ok(x) => ("", x),
            Err(x) => ("//", x),
        };
        try!(writeln!(w, "{}pub type {} = {};", comment, item.c_identifier, c_type));
    }

    Ok(())
}

fn generate_bitfields<W: Write>(w: &mut W, items: &[&library::Bitfield])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        try!(writeln!(w, "bitflags! {{\n{}#[repr(C)]\n{0}flags {}: c_uint {{",
                      tabs(1), item.c_type));
        for member in &item.members {
            let val: i64 = member.value.parse().unwrap();
            try!(writeln!(w, "{}const {} = {},", tabs(2), member.c_identifier, val as u32));
        }
        try!(writeln!(w, "{}}}\n}}", tabs(1)));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_enums<W: Write>(w: &mut W, items: &[&library::Enumeration])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        if item.members.len() == 1 {
            try!(writeln!(w, "pub type {} = c_int;", item.name));
            try!(writeln!(w, "pub const {}: {} = {};",
                          item.members[0].c_identifier, item.name, item.members[0].value));
            try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
            try!(writeln!(w, ""));
            continue;
        }

        let mut vals = HashMap::new();
        try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(C)]"));
        try!(writeln!(w, "pub enum {} {{", item.c_type));
        for member in &item.members {
            if vals.get(&member.value).is_some() {
                continue;
            }
            try!(writeln!(w, "{}{} = {},",
                          tabs(1), &prepare_enum_member_name(&member.name), member.value));
            vals.insert(member.value.clone(), member.name.clone());
        }
        try!(writeln!(w, "}}"));
        for member in &item.members {
            try!(writeln!(w, "pub const {}: {} = {1}::{};", member.c_identifier, item.c_type,
                          &prepare_enum_member_name(vals.get(&member.value).unwrap())));
        }
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_unions<W: Write>(w: &mut W, items: &[&library::Union])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        if let Some(ref c_type) = item.c_type {
            try!(writeln!(w, "pub type {} = c_void; // union", c_type));
        }
    }
    try!(writeln!(w, ""));

    Ok(())
}

fn prepare_enum_member_name(name: &str) -> String {
    let cameled = name.to_camel();
    if name.chars().next().unwrap().is_digit(10) {
        format!("_{}", cameled)
    } else {
        cameled
    }
}

fn generate_classes_structs<W: Write>(w: &mut W, classes: &[&library::Class]) -> Result<()> {
    try!(writeln!(w, ""));
    for klass in classes {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", klass.c_type));
    }

    Ok(())
}

fn generate_interfaces_structs<W: Write>(w: &mut W, interfaces: &[&library::Interface]) -> Result<()> {
    try!(writeln!(w, ""));
    for interface in interfaces {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", interface.c_type));
    }

    Ok(())
}

fn generate_records<W: Write>(w: &mut W, env: &Env, records: &[&library::Record]) -> Result<()> {
    try!(writeln!(w, ""));
    for record in records {
        let mut lines = Vec::new();
        let mut commented = false;
        let mut truncated = false;
        for field in &record.fields {
            let is_union = env.library.type_(field.typ).maybe_ref_as::<library::Union>().is_some();
            let is_bits = field.bits.is_some();
            if !truncated && (is_union || is_bits) {
                warn!("Record `{}` field `{}` not expressible in Rust, truncated",
                      record.name, field.name);
                lines.push(format!("{}_truncated_record_marker: (),", tabs(1)));
                truncated = true;
            }
            if truncated {
                if is_union {
                    lines.push(format!("{}//union,", tabs(1)));
                }
                else {
                    let bits = field.bits.map(|n| format!(": {}", n)).unwrap_or("".into());
                    lines.push(
                        format!("{}//{}: {}{},", tabs(1), field.name,
                                field.c_type.as_ref().map(|s| &s[..]).unwrap_or("fn"), bits));
                };
                continue;
            }

            let vis = if field.private { "" } else { "pub " };

            if let Some(ref c_type) = field.c_type {
                let name = mangle_keywords(&*field.name);
                let c_type = ffi_type(env, field.typ, c_type);
                lines.push(format!("{}{}{}: {},", tabs(1), vis, name, c_type.as_str()));
                if c_type.is_err() {
                    commented = true;
                }
            }
            else {
                let name = mangle_keywords(&*field.name);
                if let Some(ref func) =
                        env.library.type_(field.typ).maybe_ref_as::<library::Function>() {
                    let (com, sig) = functions::function_signature(env, func, true);
                    lines.push(format!("{}{}{}: fn{},", tabs(1), vis, name, sig));
                    commented |= com;
                }
                else if let Some(c_type) = env.library.type_(field.typ).get_glib_name() {
                    warn!("Record `{}`, field `{}` missing c:type assumed `{}`",
                          record.name, field.name, c_type);
                    let c_type = ffi_type(env, field.typ, c_type);
                    lines.push(format!("{}{}{}: {},", tabs(1), vis, name, c_type.as_str()));
                    if c_type.is_err() {
                        commented = true;
                    }
                }
                else {
                    lines.push(format!("{}{}{}: [{:?} {}],", tabs(1),
                        vis, name, field.typ, field.typ.full_name(&env.library)));
                    commented = true;
                }
            }
        }
        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {};\n", comment, record.c_type));
        }
        else {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {} {{", comment, record.c_type));
            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }
    Ok(())
}
<commit_msg>Generate constants<commit_after>use std::collections::HashMap;
use std::fs;
use std::path::*;
use std::io::{Result, Write};
use case::CaseExt;

use analysis::rust_type::parameter_rust_type;
use env::Env;
use file_saver::*;
use library;
use nameutil::*;
use super::ffi_type::ffi_type;
use super::functions;
use super::statics;
use super::super::general::{self, tabs};
use traits::*;

pub fn generate(env: &Env) {
    println!("generating sys for {}", env.config.library_name);

    let path =  PathBuf::from(&env.config.target_path)
        .join(file_name_sys("lib"));

    let parent = path.parent().unwrap();
    //TODO: do only if not exists
    let _ = fs::create_dir(parent);

    println!("Generating file {:?}", path);
    save_to_file(&path, &mut |w| generate_lib(w, env));
}

fn generate_lib<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    try!(general::start_comments(w, &env.config));
    try!(statics::begin(w));

    try!(generate_extern_crates(w, env));
    try!(statics::after_extern_crates(w));

    if env.config.library_name != "GLib" {
        try!(statics::use_glib_ffi(w));
    } else {
        try!(statics::only_for_glib(w));
    }
    if env.config.library_name == "Gtk" {
        try!(statics::only_for_gtk(w));
    }

    let ns = env.library.namespace(library::MAIN_NAMESPACE);
    let classes = prepare(ns);
    let interfaces = prepare(ns);

    try!(generate_aliases(w, env, &prepare(ns)));
    try!(generate_enums(w, &prepare(ns)));
    try!(generate_constants(w, env, &ns.constants));
    try!(generate_bitfields(w, &prepare(ns)));
    try!(generate_unions(w, &prepare(ns)));
    try!(functions::generate_callbacks(w, env, &prepare(ns)));
    try!(generate_records(w, env, &prepare(ns)));
    try!(generate_classes_structs(w, &classes));
    try!(generate_interfaces_structs(w, &interfaces));

    try!(writeln!(w, ""));
    try!(writeln!(w, "extern \"C\" {{"));
    try!(functions::generate_classes_funcs(w, env, &classes));
    try!(functions::generate_interfaces_funcs(w, env, &interfaces));
    try!(functions::generate_other_funcs(w, env, &ns.functions));

    try!(writeln!(w, "\n}}"));

    Ok(())
}

fn generate_extern_crates<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    for library_name in &env.config.external_libraries {
        try!(writeln!(w, "extern crate {0}_sys as {0}_ffi;", crate_name(library_name)));
    }

    Ok(())
}

fn prepare<T: Ord>(ns: &library::Namespace) -> Vec<&T>
where library::Type: MaybeRef<T> {
    let mut vec: Vec<&T> = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(ref x) = typ.maybe_ref() {
            vec.push(x);
        }
    }
    vec.sort();
    vec
}

fn generate_aliases<W: Write>(w: &mut W, env: &Env, items: &[&library::Alias])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        let (comment, c_type) = match ffi_type(env, item.typ, &item.target_c_type) {
            Ok(x) => ("", x),
            Err(x) => ("//", x),
        };
        try!(writeln!(w, "{}pub type {} = {};", comment, item.c_identifier, c_type));
    }

    Ok(())
}

fn generate_bitfields<W: Write>(w: &mut W, items: &[&library::Bitfield])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        try!(writeln!(w, "bitflags! {{\n{}#[repr(C)]\n{0}flags {}: c_uint {{",
                      tabs(1), item.c_type));
        for member in &item.members {
            let val: i64 = member.value.parse().unwrap();
            try!(writeln!(w, "{}const {} = {},", tabs(2), member.c_identifier, val as u32));
        }
        try!(writeln!(w, "{}}}\n}}", tabs(1)));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_constants<W: Write>(w: &mut W, env: &Env, constants: &[library::Constant]) -> Result<()> {
    try!(writeln!(w, ""));
    for constant in constants {
        let (comment, mut type_) = match parameter_rust_type(env, constant.typ, library::ParameterDirection::In) {
            Ok(x) => ("", x),
            Err(x) => ("//", x),
        };
        let mut value = constant.value.clone();
        if type_ == "&str" {
            type_ = "&'static str".into();
            value = format!("\"{}\"", escape_string(&value));
        }
        try!(writeln!(w, "{}pub const {}:{} = {};", comment,
            constant.c_identifier, type_, value));
    }

    Ok(())
}

fn escape_string(s: &str) -> String {
    let mut es = String::with_capacity(s.len() * 2);
    let _ = s.chars().map(|c| {
        match c {
            '\'' | '\"' | '\\' => {
                es.push('\\');
                es.push(c)
            }
            _ => es.push(c),
        }
    }).count();
    es
}

fn generate_enums<W: Write>(w: &mut W, items: &[&library::Enumeration])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        if item.members.len() == 1 {
            try!(writeln!(w, "pub type {} = c_int;", item.name));
            try!(writeln!(w, "pub const {}: {} = {};",
                          item.members[0].c_identifier, item.name, item.members[0].value));
            try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
            try!(writeln!(w, ""));
            continue;
        }

        let mut vals = HashMap::new();
        try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(C)]"));
        try!(writeln!(w, "pub enum {} {{", item.c_type));
        for member in &item.members {
            if vals.get(&member.value).is_some() {
                continue;
            }
            try!(writeln!(w, "{}{} = {},",
                          tabs(1), &prepare_enum_member_name(&member.name), member.value));
            vals.insert(member.value.clone(), member.name.clone());
        }
        try!(writeln!(w, "}}"));
        for member in &item.members {
            try!(writeln!(w, "pub const {}: {} = {1}::{};", member.c_identifier, item.c_type,
                          &prepare_enum_member_name(vals.get(&member.value).unwrap())));
        }
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_unions<W: Write>(w: &mut W, items: &[&library::Union])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        if let Some(ref c_type) = item.c_type {
            try!(writeln!(w, "pub type {} = c_void; // union", c_type));
        }
    }
    try!(writeln!(w, ""));

    Ok(())
}

fn prepare_enum_member_name(name: &str) -> String {
    let cameled = name.to_camel();
    if name.chars().next().unwrap().is_digit(10) {
        format!("_{}", cameled)
    } else {
        cameled
    }
}

fn generate_classes_structs<W: Write>(w: &mut W, classes: &[&library::Class]) -> Result<()> {
    try!(writeln!(w, ""));
    for klass in classes {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", klass.c_type));
    }

    Ok(())
}

fn generate_interfaces_structs<W: Write>(w: &mut W, interfaces: &[&library::Interface]) -> Result<()> {
    try!(writeln!(w, ""));
    for interface in interfaces {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", interface.c_type));
    }

    Ok(())
}

fn generate_records<W: Write>(w: &mut W, env: &Env, records: &[&library::Record]) -> Result<()> {
    try!(writeln!(w, ""));
    for record in records {
        let mut lines = Vec::new();
        let mut commented = false;
        let mut truncated = false;
        for field in &record.fields {
            let is_union = env.library.type_(field.typ).maybe_ref_as::<library::Union>().is_some();
            let is_bits = field.bits.is_some();
            if !truncated && (is_union || is_bits) {
                warn!("Record `{}` field `{}` not expressible in Rust, truncated",
                      record.name, field.name);
                lines.push(format!("{}_truncated_record_marker: (),", tabs(1)));
                truncated = true;
            }
            if truncated {
                if is_union {
                    lines.push(format!("{}//union,", tabs(1)));
                }
                else {
                    let bits = field.bits.map(|n| format!(": {}", n)).unwrap_or("".into());
                    lines.push(
                        format!("{}//{}: {}{},", tabs(1), field.name,
                                field.c_type.as_ref().map(|s| &s[..]).unwrap_or("fn"), bits));
                };
                continue;
            }

            let vis = if field.private { "" } else { "pub " };

            if let Some(ref c_type) = field.c_type {
                let name = mangle_keywords(&*field.name);
                let c_type = ffi_type(env, field.typ, c_type);
                lines.push(format!("{}{}{}: {},", tabs(1), vis, name, c_type.as_str()));
                if c_type.is_err() {
                    commented = true;
                }
            }
            else {
                let name = mangle_keywords(&*field.name);
                if let Some(ref func) =
                        env.library.type_(field.typ).maybe_ref_as::<library::Function>() {
                    let (com, sig) = functions::function_signature(env, func, true);
                    lines.push(format!("{}{}{}: fn{},", tabs(1), vis, name, sig));
                    commented |= com;
                }
                else if let Some(c_type) = env.library.type_(field.typ).get_glib_name() {
                    warn!("Record `{}`, field `{}` missing c:type assumed `{}`",
                          record.name, field.name, c_type);
                    let c_type = ffi_type(env, field.typ, c_type);
                    lines.push(format!("{}{}{}: {},", tabs(1), vis, name, c_type.as_str()));
                    if c_type.is_err() {
                        commented = true;
                    }
                }
                else {
                    lines.push(format!("{}{}{}: [{:?} {}],", tabs(1),
                        vis, name, field.typ, field.typ.full_name(&env.library)));
                    commented = true;
                }
            }
        }
        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {};\n", comment, record.c_type));
        }
        else {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {} {{", comment, record.c_type));
            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>use futures::{Async, Future, Poll, Sink};
use futures::stream::Stream;
use futures::sink::Send;
use futures_state_stream::{StateStream, StreamEvent};

use native_tls::TlsConnector;

use std::io;
use std::net::ToSocketAddrs;

use tokio::net::{ConnectFuture, TcpStream};
use tokio_io::AsyncRead;
use tokio_tls::{ConnectAsync, TlsConnectorExt};

use imap_proto::{Request, RequestId, State};
use imap_proto::builders::command::Command;
use proto::{ImapCodec, ImapTls, ImapTransport, ResponseData};

pub mod builder {
    pub use imap_proto::builders::command::{CommandBuilder, FetchBuilderAttributes,
                                            FetchBuilderMessages, FetchBuilderModifiers,
                                            FetchCommand, FetchCommandAttributes,
                                            FetchCommandMessages};
}

pub fn connect(server: &str) -> io::Result<ImapConnectFuture> {
    let addr = (server, 993).to_socket_addrs()?.next().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::Other,
            format!("no IP addresses found for {}", server),
        )
    })?;
    Ok(ImapConnectFuture::TcpConnecting(
        TcpStream::connect(&addr),
        server.to_string(),
    ))
}

pub trait ImapClient {
    type Transport: ImapTransport;
    fn into_parts(self) -> (Self::Transport, ClientState);
    fn rebuild(transport: Self::Transport, state: ClientState) -> Self;

    fn call(self, cmd: Command) -> ResponseStream<Self>
    where
        Self: ImapClient + Sized, {
        let (transport, mut state) = self.into_parts();
        let request_id = state.request_ids.next().unwrap(); // safe: never returns Err
        let (cmd_bytes, next_state) = cmd.into_parts();
        let future = transport.send(Request(request_id.clone(), cmd_bytes));
        ResponseStream::new(future, state, request_id, next_state)
    }
}

pub struct TlsClient {
    transport: ImapTls,
    state: ClientState,
}

impl ImapClient for TlsClient {
    type Transport = ImapTls;

    fn into_parts(self) -> (ImapTls, ClientState) {
        let Self { transport, state } = self;
        (transport, state)
    }

    fn rebuild(transport: ImapTls, state: ClientState) -> TlsClient {
        TlsClient { transport, state }
    }
}

pub struct ResponseStream<E>
where
    E: ImapClient, {
    future: Option<Send<E::Transport>>,
    transport: Option<E::Transport>,
    state: Option<ClientState>,
    request_id: RequestId,
    next_state: Option<State>,
    done: bool,
}

impl<E> ResponseStream<E>
where
    E: ImapClient,
{
    pub fn new(
        future: Send<E::Transport>, state: ClientState, request_id: RequestId,
        next_state: Option<State>,
    ) -> Self {
        Self {
            future: Some(future),
            transport: None,
            state: Some(state),
            request_id: request_id,
            next_state: next_state,
            done: false,
        }
    }
}

impl<E> StateStream for ResponseStream<E>
where
    E: ImapClient,
{
    type Item = ResponseData;
    type State = E;
    type Error = io::Error;
    fn poll(&mut self) -> Poll<StreamEvent<Self::Item, Self::State>, Self::Error> {
        match self.future.take() {
            Some(mut future) => match future.poll() {
                Ok(Async::Ready(transport)) => {
                    self.transport = Some(transport);
                },
                Ok(Async::NotReady) => {
                    self.future = Some(future);
                    return Ok(Async::NotReady);
                },
                Err(e) => {
                    return Err(e);
                },
            },
            None => {},
        }
        let mut transport = match self.transport.take() {
            None => return Ok(Async::NotReady),
            Some(mut transport) => transport,
        };
        if self.done {
            let mut state = self.state.take().unwrap(); // safe: initialized from start
            if let Some(next_state) = self.next_state.take() {
                state.state = next_state;
            }
            return Ok(Async::Ready(StreamEvent::Done(E::rebuild(
                transport,
                state,
            ))));
        }
        match transport.poll() {
            Ok(Async::Ready(Some(rsp))) => {
                if let Some(req_id) = rsp.request_id() {
                    self.done = *req_id == self.request_id;
                };
                self.transport = Some(transport);
                return Ok(Async::Ready(StreamEvent::Next(rsp)));
            },
            Err(e) => {
                return Err(e);
            },
            _ => (),
        }
        self.transport = Some(transport);
        Ok(Async::NotReady)
    }
}

pub enum ImapConnectFuture {
    #[doc(hidden)]
    TcpConnecting(ConnectFuture, String),
    #[doc(hidden)]
    TlsHandshake(ConnectAsync<TcpStream>),
    #[doc(hidden)]
    ServerGreeting(Option<ImapTls>),
}

impl Future for ImapConnectFuture {
    type Item = (TlsClient, ResponseData);
    type Error = io::Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut new = None;
        if let ImapConnectFuture::TcpConnecting(ref mut future, ref domain) = *self {
            let stream = try_ready!(future.poll());
            let ctx = TlsConnector::builder().unwrap().build().unwrap();
            let future = ctx.connect_async(domain, stream);
            new = Some(ImapConnectFuture::TlsHandshake(future));
        }
        if new.is_some() {
            *self = new.take().unwrap();
        }
        if let ImapConnectFuture::TlsHandshake(ref mut future) = *self {
            let transport = try_ready!(
                future
                    .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
                    .poll()
            ).framed(ImapCodec::default());
            new = Some(ImapConnectFuture::ServerGreeting(Some(transport)));
        }
        if new.is_some() {
            *self = new.take().unwrap();
        }
        if let ImapConnectFuture::ServerGreeting(ref mut wrapped) = *self {
            let msg = try_ready!(wrapped.as_mut().unwrap().poll()).unwrap();
            return Ok(Async::Ready((
                TlsClient {
                    transport: wrapped.take().unwrap(),
                    state: ClientState::new(),
                },
                msg,
            )));
        }
        Ok(Async::NotReady)
    }
}

pub struct ClientState {
    state: State,
    request_ids: IdGenerator,
}

impl ClientState {
    pub fn new() -> Self {
        Self {
            state: State::NotAuthenticated,
            request_ids: IdGenerator::new(),
        }
    }
}

impl Default for ClientState {
    fn default() -> Self {
        Self::new()
    }
}

pub struct IdGenerator {
    next: u64,
}

impl IdGenerator {
    pub fn new() -> Self {
        Self { next: 0 }
    }
}

impl Default for IdGenerator {
    fn default() -> Self {
        Self::new()
    }
}

impl Iterator for IdGenerator {
    type Item = RequestId;
    fn next(&mut self) -> Option<Self::Item> {
        self.next += 1;
        Some(RequestId(format!("A{:04}", self.next % 10_000)))
    }
}
<commit_msg>Simplify code as suggested by clippy<commit_after>use futures::{Async, Future, Poll, Sink};
use futures::stream::Stream;
use futures::sink::Send;
use futures_state_stream::{StateStream, StreamEvent};

use native_tls::TlsConnector;

use std::io;
use std::net::ToSocketAddrs;

use tokio::net::{ConnectFuture, TcpStream};
use tokio_io::AsyncRead;
use tokio_tls::{ConnectAsync, TlsConnectorExt};

use imap_proto::{Request, RequestId, State};
use imap_proto::builders::command::Command;
use proto::{ImapCodec, ImapTls, ImapTransport, ResponseData};

pub mod builder {
    pub use imap_proto::builders::command::{CommandBuilder, FetchBuilderAttributes,
                                            FetchBuilderMessages, FetchBuilderModifiers,
                                            FetchCommand, FetchCommandAttributes,
                                            FetchCommandMessages};
}

pub fn connect(server: &str) -> io::Result<ImapConnectFuture> {
    let addr = (server, 993).to_socket_addrs()?.next().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::Other,
            format!("no IP addresses found for {}", server),
        )
    })?;
    Ok(ImapConnectFuture::TcpConnecting(
        TcpStream::connect(&addr),
        server.to_string(),
    ))
}

pub trait ImapClient {
    type Transport: ImapTransport;
    fn into_parts(self) -> (Self::Transport, ClientState);
    fn rebuild(transport: Self::Transport, state: ClientState) -> Self;

    fn call(self, cmd: Command) -> ResponseStream<Self>
    where
        Self: ImapClient + Sized, {
        let (transport, mut state) = self.into_parts();
        let request_id = state.request_ids.next().unwrap(); // safe: never returns Err
        let (cmd_bytes, next_state) = cmd.into_parts();
        let future = transport.send(Request(request_id.clone(), cmd_bytes));
        ResponseStream::new(future, state, request_id, next_state)
    }
}

pub struct TlsClient {
    transport: ImapTls,
    state: ClientState,
}

impl ImapClient for TlsClient {
    type Transport = ImapTls;

    fn into_parts(self) -> (ImapTls, ClientState) {
        let Self { transport, state } = self;
        (transport, state)
    }

    fn rebuild(transport: ImapTls, state: ClientState) -> TlsClient {
        TlsClient { transport, state }
    }
}

pub struct ResponseStream<E>
where
    E: ImapClient, {
    future: Option<Send<E::Transport>>,
    transport: Option<E::Transport>,
    state: Option<ClientState>,
    request_id: RequestId,
    next_state: Option<State>,
    done: bool,
}

impl<E> ResponseStream<E>
where
    E: ImapClient,
{
    pub fn new(
        future: Send<E::Transport>, state: ClientState, request_id: RequestId,
        next_state: Option<State>,
    ) -> Self {
        Self {
            future: Some(future),
            transport: None,
            state: Some(state),
            request_id: request_id,
            next_state: next_state,
            done: false,
        }
    }
}

impl<E> StateStream for ResponseStream<E>
where
    E: ImapClient,
{
    type Item = ResponseData;
    type State = E;
    type Error = io::Error;
    fn poll(&mut self) -> Poll<StreamEvent<Self::Item, Self::State>, Self::Error> {
        if let Some(mut future) = self.future.take() {
            match future.poll() {
                Ok(Async::Ready(transport)) => {
                    self.transport = Some(transport);
                },
                Ok(Async::NotReady) => {
                    self.future = Some(future);
                    return Ok(Async::NotReady);
                },
                Err(e) => {
                    return Err(e);
                },
            }
        }
        let mut transport = match self.transport.take() {
            None => return Ok(Async::NotReady),
            Some(mut transport) => transport,
        };
        if self.done {
            let mut state = self.state.take().unwrap(); // safe: initialized from start
            if let Some(next_state) = self.next_state.take() {
                state.state = next_state;
            }
            return Ok(Async::Ready(StreamEvent::Done(E::rebuild(
                transport,
                state,
            ))));
        }
        match transport.poll() {
            Ok(Async::Ready(Some(rsp))) => {
                if let Some(req_id) = rsp.request_id() {
                    self.done = *req_id == self.request_id;
                };
                self.transport = Some(transport);
                return Ok(Async::Ready(StreamEvent::Next(rsp)));
            },
            Err(e) => {
                return Err(e);
            },
            _ => (),
        }
        self.transport = Some(transport);
        Ok(Async::NotReady)
    }
}

pub enum ImapConnectFuture {
    #[doc(hidden)]
    TcpConnecting(ConnectFuture, String),
    #[doc(hidden)]
    TlsHandshake(ConnectAsync<TcpStream>),
    #[doc(hidden)]
    ServerGreeting(Option<ImapTls>),
}

impl Future for ImapConnectFuture {
    type Item = (TlsClient, ResponseData);
    type Error = io::Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut new = None;
        if let ImapConnectFuture::TcpConnecting(ref mut future, ref domain) = *self {
            let stream = try_ready!(future.poll());
            let ctx = TlsConnector::builder().unwrap().build().unwrap();
            let future = ctx.connect_async(domain, stream);
            new = Some(ImapConnectFuture::TlsHandshake(future));
        }
        if new.is_some() {
            *self = new.take().unwrap();
        }
        if let ImapConnectFuture::TlsHandshake(ref mut future) = *self {
            let transport = try_ready!(
                future
                    .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
                    .poll()
            ).framed(ImapCodec::default());
            new = Some(ImapConnectFuture::ServerGreeting(Some(transport)));
        }
        if new.is_some() {
            *self = new.take().unwrap();
        }
        if let ImapConnectFuture::ServerGreeting(ref mut wrapped) = *self {
            let msg = try_ready!(wrapped.as_mut().unwrap().poll()).unwrap();
            return Ok(Async::Ready((
                TlsClient {
                    transport: wrapped.take().unwrap(),
                    state: ClientState::new(),
                },
                msg,
            )));
        }
        Ok(Async::NotReady)
    }
}

pub struct ClientState {
    state: State,
    request_ids: IdGenerator,
}

impl ClientState {
    pub fn new() -> Self {
        Self {
            state: State::NotAuthenticated,
            request_ids: IdGenerator::new(),
        }
    }
}

impl Default for ClientState {
    fn default() -> Self {
        Self::new()
    }
}

pub struct IdGenerator {
    next: u64,
}

impl IdGenerator {
    pub fn new() -> Self {
        Self { next: 0 }
    }
}

impl Default for IdGenerator {
    fn default() -> Self {
        Self::new()
    }
}

impl Iterator for IdGenerator {
    type Item = RequestId;
    fn next(&mut self) -> Option<Self::Item> {
        self.next += 1;
        Some(RequestId(format!("A{:04}", self.next % 10_000)))
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The Rouille developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Apply content encodings (such as gzip compression) to the response.
//!
//! This module provides access to the content encodings supported by a request as well as
//! a function to automatically apply common content encodings to a response.
//! # Basic example
//!
//! Here is a basic example showing how to use content encodings:
//!
//! ```
//! use rouille::Request;
//! use rouille::Response;
//! use rouille::content_encoding;
//!
//! fn handle_request(request: &Request) -> Response {
//!     let response = Response::text("Hello world");
//!     content_encoding::apply(&request, response)
//! }
//! ```
use input;
use Request;
use Response;

/// Applies content encoding to the response.
///
/// Analyzes the `Accept-Encoding` header of the request. If one of the encodings is recognized and
/// supported by rouille, it adds a `Content-Encoding` header to the `Response` and encodes its
/// body.
///
/// If the response already has a `Content-Encoding` header, this function is a no-op.
/// If the response has a `Content-Type` header that isn't textual content, this function is a
/// no-op.
///
/// The gzip encoding is supported only if you enable the `gzip` feature of rouille (which is
/// enabled by default).
///
/// # Example
///
/// ```rust
/// use rouille::content_encoding;
/// use rouille::Request;
/// use rouille::Response;
///
/// fn handle(request: &Request) -> Response {
///     content_encoding::apply(request, Response::text("hello world"))
/// }
/// ```
pub fn apply(request: &Request, mut response: Response) -> Response {
    // Only text should be encoded. Otherwise just return.
    if !response_is_text(&response) {
        return response;
    }

    // If any of the response's headers is equal to `Content-Encoding`, ignore the function
    // call and return immediately.
    if response
        .headers
        .iter()
        .any(|&(ref key, _)| key.eq_ignore_ascii_case("Content-Encoding"))
    {
        return response;
    }

    // Now let's get the list of content encodings accepted by the request.
    // The list should be ordered from the most desired to the least desired.
    let encoding_preference = ["br", "gzip", "x-gzip", "identity"];
    let accept_encoding_header = request.header("Accept-Encoding").unwrap_or("");
    if let Some(preferred_index) = input::priority_header_preferred(
        accept_encoding_header,
        encoding_preference.iter().cloned(),
    ) {
        match encoding_preference[preferred_index] {
            "br" => brotli(&mut response),
            "gzip" | "x-gzip" => gzip(&mut response),
            _ => (),
        }
    }

    response
}

// Returns true if the Content-Type of the response is a type that should be encoded.
// Since encoding is purely an optimization, it's not a problem if the function sometimes has
// false positives or false negatives.
fn response_is_text(response: &Response) -> bool {
    response.headers.iter().any(|&(ref key, ref value)| {
        if !key.eq_ignore_ascii_case("Content-Type") {
            return false;
        }

        let content_type = value.to_lowercase();
        content_type.starts_with("text/")
            || content_type.contains("javascript")
            || content_type.contains("json")
            || content_type.contains("xml")
            || content_type.contains("font")
    })
}

#[cfg(feature = "gzip")]
fn gzip(response: &mut Response) {
    use deflate::deflate_bytes_gzip;
    use std::io;
    use std::mem;
    use ResponseBody;

    response
        .headers
        .push(("Content-Encoding".into(), "gzip".into()));
    let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
    let (mut raw_data, size) = previous_body.into_reader_and_size();
    let mut src = match size {
        Some(size) => Vec::with_capacity(size),
        None => Vec::new(),
    };
    io::copy(&mut raw_data, &mut src).expect("Failed reading response body while gzipping");
    let zipped = deflate_bytes_gzip(&src);
    response.data = ResponseBody::from_data(zipped);
}

#[cfg(not(feature = "gzip"))]
#[inline]
fn gzip(_: &mut Response) {}

#[cfg(feature = "brotli")]
fn brotli(response: &mut Response) {
    use brotli::enc::reader::CompressorReader;
    use std::mem;
    use ResponseBody;

    response
        .headers
        .push(("Content-Encoding".into(), "br".into()));
    let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
    let (raw_data, _) = previous_body.into_reader_and_size();
    // Using default Brotli parameters: 0 buffer_size == 4096, compression level 6, lgwin == 22
    response.data = ResponseBody::from_reader(CompressorReader::new(raw_data, 0, 6, 22));
}

#[cfg(not(feature = "brotli"))]
#[inline]
fn brotli(_: &mut Response) {}

#[cfg(test)]
mod tests {
    use content_encoding;
    use Request;
    use Response;

    // TODO: more tests for encoding stuff
    #[test]
    fn text_response() {
        assert!(content_encoding::response_is_text(&Response::text("")));
    }

    #[test]
    fn non_text_response() {
        assert!(!content_encoding::response_is_text(&Response::from_data(
            "image/jpeg",
            ""
        )));
    }

    #[test]
    fn no_req_encodings() {
        let request = Request::fake_http("GET", "/", vec![], vec![]);
        let response = Response::html("
Hello world
");
        let encoded_response = content_encoding::apply(&request, response);
        assert!(!encoded_response
            .headers
            .iter()
            .any(|(header_name, _)| header_name == "Content-Encoding")); // No Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();
        assert_eq!(
            String::from_utf8(encoded_content).unwrap(),
            "
Hello world
"
        ); // No encoding applied
    }

    #[test]
    fn empty_req_encodings() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html("
Hello world
");

        let encoded_response = content_encoding::apply(&request, response);
        assert!(!encoded_response
            .headers
            .iter()
            .any(|(header_name, _)| header_name == "Content-Encoding")); // No Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();
        assert_eq!(
            String::from_utf8(encoded_content).unwrap(),
            "
Hello world
"
        ); // No encoding applied
    }

    #[test]
    fn multi_req_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "foo".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html("
Hello world
");

        let encoded_response = content_encoding::apply(&request, response);
        assert!(!encoded_response
            .headers
            .iter()
            .any(|(header_name, _)| header_name == "Content-Encoding")); // No Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();
        assert_eq!(
            String::from_utf8(encoded_content).unwrap(),
            "
Hello world
"
        ); // No encoding applied
    }

    #[test]
    fn unknown_req_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "x-gzip, br".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html("
Hello world
");

        let encoded_response = content_encoding::apply(&request, response);
        assert!(encoded_response
            .headers
            .contains(&("Content-Encoding".into(), "br".into()))); // Brotli Content-Encoding header
    }

    #[test]
    fn brotli_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "br".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html(
            "<title>Hello world</title>
Hello world
",
        );

        let encoded_response = content_encoding::apply(&request, response);
        assert!(encoded_response
            .headers
            .contains(&("Content-Encoding".into(), "br".into()))); // Brotli Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();
        assert_eq!(
            encoded_content,
            vec![
                27, 75, 0, 0, 4, 28, 114, 164, 129, 5, 210, 206, 25, 30, 90, 114, 224, 114, 73,
                109, 45, 196, 23, 126, 240, 144, 77, 40, 26, 211, 228, 67, 73, 40, 236, 55, 101,
                254, 127, 147, 194, 129, 132, 65, 130, 120, 152, 249, 68, 56, 93, 2
            ]
        ); // Applied proper Brotli encoding
    }

    #[test]
    fn gzip_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "gzip".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html(
            "<title>Hello world</title>
Hello world
",
        );

        let encoded_response = content_encoding::apply(&request, response);
        assert!(encoded_response
            .headers
            .contains(&("Content-Encoding".into(), "gzip".into()))); // gzip Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();
        assert_eq!(
            encoded_content,
            vec![
                31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 179, 201, 40, 201, 205, 177, 179, 201, 72, 77, 76,
                177, 179, 41, 201, 44, 201, 73, 181, 243, 72, 205, 201, 201, 87, 40, 207, 47, 202,
                73, 177, 209, 135, 8, 217, 36, 229, 167, 84, 218, 217, 20, 160, 202, 21, 216, 217,
                232, 67, 36, 244, 193, 166, 0, 0, 202, 239, 44, 120, 76, 0, 0, 0
            ]
        ); // Applied proper gzip encoding
    }
}
<commit_msg>content-encoding: fix Gzip test on non-linux hosts<commit_after>// Copyright (c) 2016 The Rouille developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Apply content encodings (such as gzip compression) to the response.
//!
//! This module provides access to the content encodings supported by a request as well as
//! a function to automatically apply common content encodings to a response.
//! # Basic example
//!
//! Here is a basic example showing how to use content encodings:
//!
//! ```
//! use rouille::Request;
//! use rouille::Response;
//! use rouille::content_encoding;
//!
//! fn handle_request(request: &Request) -> Response {
//!     let response = Response::text("Hello world");
//!     content_encoding::apply(&request, response)
//! }
//! ```
use input;
use Request;
use Response;

/// Applies content encoding to the response.
///
/// Analyzes the `Accept-Encoding` header of the request. If one of the encodings is recognized and
/// supported by rouille, it adds a `Content-Encoding` header to the `Response` and encodes its
/// body.
///
/// If the response already has a `Content-Encoding` header, this function is a no-op.
/// If the response has a `Content-Type` header that isn't textual content, this function is a
/// no-op.
///
/// The gzip encoding is supported only if you enable the `gzip` feature of rouille (which is
/// enabled by default).
///
/// # Example
///
/// ```rust
/// use rouille::content_encoding;
/// use rouille::Request;
/// use rouille::Response;
///
/// fn handle(request: &Request) -> Response {
///     content_encoding::apply(request, Response::text("hello world"))
/// }
/// ```
pub fn apply(request: &Request, mut response: Response) -> Response {
    // Only text should be encoded. Otherwise just return.
    if !response_is_text(&response) {
        return response;
    }

    // If any of the response's headers is equal to `Content-Encoding`, ignore the function
    // call and return immediately.
    if response
        .headers
        .iter()
        .any(|&(ref key, _)| key.eq_ignore_ascii_case("Content-Encoding"))
    {
        return response;
    }

    // Now let's get the list of content encodings accepted by the request.
    // The list should be ordered from the most desired to the least desired.
    let encoding_preference = ["br", "gzip", "x-gzip", "identity"];
    let accept_encoding_header = request.header("Accept-Encoding").unwrap_or("");
    if let Some(preferred_index) = input::priority_header_preferred(
        accept_encoding_header,
        encoding_preference.iter().cloned(),
    ) {
        match encoding_preference[preferred_index] {
            "br" => brotli(&mut response),
            "gzip" | "x-gzip" => gzip(&mut response),
            _ => (),
        }
    }

    response
}

// Returns true if the Content-Type of the response is a type that should be encoded.
// Since encoding is purely an optimization, it's not a problem if the function sometimes has
// false positives or false negatives.
fn response_is_text(response: &Response) -> bool {
    response.headers.iter().any(|&(ref key, ref value)| {
        if !key.eq_ignore_ascii_case("Content-Type") {
            return false;
        }

        let content_type = value.to_lowercase();
        content_type.starts_with("text/")
            || content_type.contains("javascript")
            || content_type.contains("json")
            || content_type.contains("xml")
            || content_type.contains("font")
    })
}

#[cfg(feature = "gzip")]
fn gzip(response: &mut Response) {
    use deflate::deflate_bytes_gzip;
    use std::io;
    use std::mem;
    use ResponseBody;

    response
        .headers
        .push(("Content-Encoding".into(), "gzip".into()));
    let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
    let (mut raw_data, size) = previous_body.into_reader_and_size();
    let mut src = match size {
        Some(size) => Vec::with_capacity(size),
        None => Vec::new(),
    };
    io::copy(&mut raw_data, &mut src).expect("Failed reading response body while gzipping");
    let zipped = deflate_bytes_gzip(&src);
    response.data = ResponseBody::from_data(zipped);
}

#[cfg(not(feature = "gzip"))]
#[inline]
fn gzip(_: &mut Response) {}

#[cfg(feature = "brotli")]
fn brotli(response: &mut Response) {
    use brotli::enc::reader::CompressorReader;
    use std::mem;
    use ResponseBody;

    response
        .headers
        .push(("Content-Encoding".into(), "br".into()));
    let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
    let (raw_data, _) = previous_body.into_reader_and_size();
    // Using default Brotli parameters: 0 buffer_size == 4096, compression level 6, lgwin == 22
    response.data = ResponseBody::from_reader(CompressorReader::new(raw_data, 0, 6, 22));
}

#[cfg(not(feature = "brotli"))]
#[inline]
fn brotli(_: &mut Response) {}

#[cfg(test)]
mod tests {
    use content_encoding;
    use Request;
    use Response;

    // TODO: more tests for encoding stuff
    #[test]
    fn text_response() {
        assert!(content_encoding::response_is_text(&Response::text("")));
    }

    #[test]
    fn non_text_response() {
        assert!(!content_encoding::response_is_text(&Response::from_data(
            "image/jpeg",
            ""
        )));
    }

    #[test]
    fn no_req_encodings() {
        let request = Request::fake_http("GET", "/", vec![], vec![]);
        let response = Response::html("
Hello world
");
        let encoded_response = content_encoding::apply(&request, response);
        assert!(!encoded_response
            .headers
            .iter()
            .any(|(header_name, _)| header_name == "Content-Encoding")); // No Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();
        assert_eq!(
            String::from_utf8(encoded_content).unwrap(),
            "
Hello world
"
        ); // No encoding applied
    }

    #[test]
    fn empty_req_encodings() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html("
Hello world
");

        let encoded_response = content_encoding::apply(&request, response);
        assert!(!encoded_response
            .headers
            .iter()
            .any(|(header_name, _)| header_name == "Content-Encoding")); // No Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();
        assert_eq!(
            String::from_utf8(encoded_content).unwrap(),
            "
Hello world
"
        ); // No encoding applied
    }

    #[test]
    fn multi_req_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "foo".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html("
Hello world
");

        let encoded_response = content_encoding::apply(&request, response);
        assert!(!encoded_response
            .headers
            .iter()
            .any(|(header_name, _)| header_name == "Content-Encoding")); // No Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();
        assert_eq!(
            String::from_utf8(encoded_content).unwrap(),
            "
Hello world
"
        ); // No encoding applied
    }

    #[test]
    fn unknown_req_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "x-gzip, br".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html("
Hello world
");

        let encoded_response = content_encoding::apply(&request, response);
        assert!(encoded_response
            .headers
            .contains(&("Content-Encoding".into(), "br".into()))); // Brotli Content-Encoding header
    }

    #[test]
    fn brotli_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "br".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html(
            "<title>Hello world</title>
Hello world
",
        );

        let encoded_response = content_encoding::apply(&request, response);
        assert!(encoded_response
            .headers
            .contains(&("Content-Encoding".into(), "br".into()))); // Brotli Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();
        assert_eq!(
            encoded_content,
            vec![
                27, 75, 0, 0, 4, 28, 114, 164, 129, 5, 210, 206, 25, 30, 90, 114, 224, 114, 73,
                109, 45, 196, 23, 126, 240, 144, 77, 40, 26, 211, 228, 67, 73, 40, 236, 55, 101,
                254, 127, 147, 194, 129, 132, 65, 130, 120, 152, 249, 68, 56, 93, 2
            ]
        ); // Applied proper Brotli encoding
    }

    #[test]
    fn gzip_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "gzip".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };
        let response = Response::html(
            "<title>Hello world</title>
Hello world
",
        );

        let encoded_response = content_encoding::apply(&request, response);
        assert!(encoded_response
            .headers
            .contains(&("Content-Encoding".into(), "gzip".into()))); // gzip Content-Encoding header
        let mut encoded_content = vec![];
        encoded_response
            .data
            .into_reader_and_size()
            .0
            .read_to_end(&mut encoded_content)
            .unwrap();

        // The 10-byte Gzip header contains an OS ID and a 4 byte timestamp
        // which are not stable, so we skip them in this comparison. Doing a
        // literal compare here is slightly silly, but the `deflate` crate has
        // no public decompressor functions for us to test a round-trip
        assert_eq!(
            encoded_content[10..],
            vec![
                179, 201, 40, 201, 205, 177, 179, 201, 72, 77, 76, 177, 179, 41, 201, 44, 201, 73,
                181, 243, 72, 205, 201, 201, 87, 40, 207, 47, 202, 73, 177, 209, 135, 8, 217, 36,
                229, 167, 84, 218, 217, 20, 160, 202, 21, 216, 217, 232, 67, 36, 244, 193, 166, 0,
                0, 202, 239, 44, 120, 76, 0, 0, 0
            ]
        ); // Applied proper gzip encoding
    }
}
<|endoftext|>


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

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

use crate::core::codepointinfo::CodepointInfo;

pub type LineCellIndex = usize;

/// A LineCell encapsulates code point information (CodepoinInfo).

/// The displayed Lines are composed of LineCell
#[derive(Hash, Default, Debug, Clone, Eq, PartialEq)]
pub struct LineCell {
    pub cpi: CodepointInfo,
    pub is_used: bool,
}

impl LineCell {
    pub fn new() -> Self {
        LineCell {
            cpi: CodepointInfo::new(),
            is_used: false,
        }
    }
}

/// A Line is an array of LineCell and some metadata.

/// A Screen is composed of Line(s)
#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct Line {
    pub cells: Vec<LineCell>,
    pub nb_cells: usize,
    pub max_width: usize,
    pub start_index: usize,
    width: usize,
    pub read_only: bool,
    pub metadata: bool,
    hash_cache: u64,
    hash_unclipped_cache: u64,
}

impl Line {
    pub fn new(width: LineCellIndex) -> Self {
        assert_eq!(width > 0, true);

        let mut cells = Vec::with_capacity(width);
        for _ in 0..width {
            cells.push(LineCell::new());
        }

        Line {
            cells,
            nb_cells: 0,
            max_width: width,
            start_index: 0,
            width,
            read_only: false,
            metadata: false,
            hash_cache: 0,
            hash_unclipped_cache: 0,
        }
    }

    pub fn hash(&mut self) -> u64 {
        if self.hash_cache != 0 {
            return self.hash_cache;
        }

        let mut s = DefaultHasher::new();

        for i in 0..self.width() {
            self.cells[self.start_index + i].hash(&mut s);
        }

        self.hash_cache = s.finish();
        self.hash_cache
    }

    pub fn hash_unclipped(&mut self) -> u64 {
        if self.hash_unclipped_cache != 0 {
            return self.hash_unclipped_cache;
        }

        let mut s = DefaultHasher::new();

        for i in 0..self.max_width {
            self.cells[i].hash(&mut s);
        }
        self.hash_unclipped_cache = s.finish();
        self.hash_unclipped_cache
    }

    pub fn width(&self) -> usize {
        self.width - self.start_index
    }

    pub fn max_width(&self) -> usize {
        self.max_width
    }

    pub fn resize(&mut self, width: LineCellIndex) {
        self.cells.resize(width, LineCell::new());
        self.nb_cells = 0;
        self.max_width = width;
        self.start_index = 0;
        self.width = width;
        self.read_only = false;
        self.metadata = false;
        self.hash_cache = 0;
    }

    /// returns (start_index, width) tupple
    /// [ 0 <= start_index < width <= screen.max_width() ]
    pub fn clipping(&mut self) -> (usize, usize) {
        (self.start_index, self.width)
    }

    /// [ 0 <= start_index < width <= screen.max_width() ]
    pub fn set_clipping(&mut self, start_index: usize, width: usize) {
        assert!(start_index < self.max_width);
        assert!(width <= self.max_width);
        assert!(start_index + width <= self.max_width);

        self.start_index = start_index;
        self.width = start_index + width;
        self.hash_cache = 0;
        self.nb_cells = 0;
        self.read_only = false;
    }

    pub fn clear_clip_width(&mut self) {
        self.set_clipping(0, self.max_width);
    }

    pub fn push(&mut self, cpi: CodepointInfo) -> (bool, LineCellIndex) {
        if self.nb_cells < self.width() && !self.read_only {
            self.cells[self.start_index + self.nb_cells].cpi = cpi;
            self.cells[self.start_index + self.nb_cells].is_used = true;

            self.nb_cells += 1;

            if self.nb_cells == self.width {
                self.read_only = true;
            }

            (true, self.nb_cells)
        } else {
            self.read_only = true;
            (false, self.nb_cells)
        }
    }

    pub fn clear(&mut self) {
        for i in 0..self.width() {
            self.cells[self.start_index + i] = LineCell::new();
        }

        self.hash_cache = 0;
        self.nb_cells = 0;
        self.read_only = false;
        self.metadata = false;
        self.hash_cache = 0;
    }

    pub fn get_first_cpi(&self) -> Option<&CodepointInfo> {
        if self.start_index < self.width {
            Some(&self.cells[self.start_index].cpi)
        } else {
            // internal error
            None
        }
    }

    pub fn get_last_cpi(&self) -> Option<&CodepointInfo> {
        if self.nb_cells > 0 {
            Some(&self.cells[self.start_index + self.nb_cells - 1].cpi)
        } else {
            None
        }
    }

    pub fn get_cpi(&self, index: LineCellIndex) -> Option<&CodepointInfo> {
        if index < self.width() {
            Some(&self.cells[self.start_index + index].cpi)
        } else {
            None
        }
    }

    pub fn get_unclipped_cpi(&self, index: LineCellIndex) -> Option<&CodepointInfo> {
        if index < self.max_width() {
            Some(&self.cells[index].cpi)
        } else {
            None
        }
    }

    pub fn get_mut_unclipped_cpi(&mut self, index: LineCellIndex) -> Option<&mut CodepointInfo> {
        if index < self.max_width() {
            Some(&mut self.cells[index].cpi)
        } else {
            None
        }
    }

    pub fn get_mut_cpi(&mut self, index: LineCellIndex) -> Option<&mut CodepointInfo> {
        if index < self.width() {
            Some(&mut self.cells[self.start_index + index].cpi)
        } else {
            None
        }
    }

    pub fn get_used_cpi(&self, index: LineCellIndex) -> Option<&CodepointInfo> {
        if index < self.nb_cells {
            self.get_cpi(index)
        } else {
            None
        }
    }

    pub fn get_mut_used_cpi(&mut self, index: LineCellIndex) -> Option<&mut CodepointInfo> {
        if index < self.nb_cells {
            self.get_mut_cpi(index)
        } else {
            None
        }
    }

    pub fn get_used_cpi_clipped(
        &self,
        index: LineCellIndex,
    ) -> (Option<&CodepointInfo>, LineCellIndex) {
        if self.nb_cells == 0 {
            return (None, 0);
        }

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

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

use crate::core::codepointinfo::CodepointInfo;

pub type LineCellIndex = usize;

/// A LineCell encapsulates code point information (CodepoinInfo).

/// The displayed Lines are composed of LineCell
#[derive(Hash, Default, Debug, Clone, Eq, PartialEq)]
pub struct LineCell {
    pub cpi: CodepointInfo,
    pub is_used: bool,
}

impl LineCell {
    pub fn new() -> Self {
        LineCell {
            cpi: CodepointInfo::new(),
            is_used: false,
        }
    }
}

/// A Line is an array of LineCell and some metadata.

/// A Screen is composed of Line(s)
#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct Line {
    pub cells: Vec<LineCell>,
    pub nb_cells: usize,
    pub max_width: usize,
    pub start_index: usize,
    width: usize,
    pub read_only: bool,
    pub metadata: bool,
    hash_cache: u64,
    hash_unclipped_cache: u64,
}

impl Line {
    pub fn new(width: LineCellIndex) -> Self {
        assert_eq!(width > 0, true);

        let mut cells = Vec::with_capacity(width);
        for _ in 0..width {
            cells.push(LineCell::new());
        }

        Line {
            cells,
            nb_cells: 0,
            max_width: width,
            start_index: 0,
            width,
            read_only: false,
            metadata: false,
            hash_cache: 0,
            hash_unclipped_cache: 0,
        }
    }

    pub fn hash(&mut self) -> u64 {
        if self.hash_cache != 0 {
            return self.hash_cache;
        }

        let mut s = DefaultHasher::new();

        for i in 0..self.width() {
            self.cells[self.start_index + i].hash(&mut s);
        }

        self.hash_cache = s.finish();
        self.hash_cache
    }

    pub fn hash_unclipped(&mut self) -> u64 {
        if self.hash_unclipped_cache != 0 {
            return self.hash_unclipped_cache;
        }

        let mut s = DefaultHasher::new();

        for i in 0..self.max_width {
            self.cells[i].hash(&mut s);
        }
        self.hash_unclipped_cache = s.finish();
        self.hash_unclipped_cache
    }

    pub fn width(&self) -> usize {
        self.width - self.start_index
    }

    pub fn max_width(&self) -> usize {
        self.max_width
    }

    pub fn resize(&mut self, width: LineCellIndex) {
        self.cells.resize(width, LineCell::new());
        self.nb_cells = 0;
        self.max_width = width;
        self.start_index = 0;
        self.width = width;
        self.read_only = false;
        self.metadata = false;
        self.hash_cache = 0;
    }

    /// returns (start_index, width) tupple
    /// [ 0 <= start_index < width <= screen.max_width() ]
    pub fn clipping(&mut self) -> (usize, usize) {
        (self.start_index, self.width)
    }

    /// [ 0 <= start_index < width <= screen.max_width() ]
    pub fn set_clipping(&mut self, start_index: usize, width: usize) {
        assert!(start_index < self.max_width);
        assert!(width <= self.max_width);
        assert!(start_index + width <= self.max_width);

        self.start_index = start_index;
        self.width = start_index + width;
        self.hash_cache = 0;
        self.nb_cells = 0;
        self.read_only = false;
    }

    pub fn clear_clip_width(&mut self) {
        self.set_clipping(0, self.max_width);
    }

    pub fn push(&mut self, cpi: CodepointInfo) -> (bool, LineCellIndex) {
        if self.nb_cells < self.width() && !self.read_only {
            self.cells[self.start_index + self.nb_cells].cpi = cpi;
            self.cells[self.start_index + self.nb_cells].is_used = true;

            self.nb_cells += 1;

            if self.nb_cells == self.width {
                self.read_only = true;
            }

            (true, self.nb_cells)
        } else {
            self.read_only = true;
            (false, self.nb_cells)
        }
    }

    pub fn clear(&mut self) {
        for i in 0..self.width() {
            self.cells[self.start_index + i] = LineCell::new();
        }

        self.hash_cache = 0;
        self.nb_cells = 0;
        self.read_only = false;
        self.metadata = false;
        self.hash_cache = 0;
    }

    pub fn get_first_cpi(&self) -> Option<&CodepointInfo> {
        if self.start_index < self.width {
            Some(&self.cells[self.start_index].cpi)
        } else {
            // internal error
            None
        }
    }

    pub fn get_last_cpi(&self) -> Option<&CodepointInfo> {
        if self.nb_cells > 0 {
            Some(&self.cells[self.start_index + self.nb_cells - 1].cpi)
        } else {
            None
        }
    }

    pub fn get_unclipped_cpi(&self, index: LineCellIndex) -> Option<&CodepointInfo> {
        if index < self.max_width() {
            Some(&self.cells[index].cpi)
        } else {
            None
        }
    }

    pub fn get_mut_unclipped_cpi(&mut self, index: LineCellIndex) -> Option<&mut CodepointInfo> {
        if index < self.max_width() {
            Some(&mut self.cells[index].cpi)
        } else {
            None
        }
    }

    pub fn get_cpi(&self, index: LineCellIndex) -> Option<&CodepointInfo> {
        if index < self.width() {
            Some(&self.cells[self.start_index + index].cpi)
        } else {
            None
        }
    }

    pub fn get_mut_cpi(&mut self, index: LineCellIndex) -> Option<&mut CodepointInfo> {
        if index < self.width() {
            Some(&mut self.cells[self.start_index + index].cpi)
        } else {
            None
        }
    }

    pub fn get_used_cpi(&self, index: LineCellIndex) -> Option<&CodepointInfo> {
        if index < self.nb_cells {
            self.get_cpi(index)
        } else {
            None
        }
    }

    pub fn get_mut_used_cpi(&mut self, index: LineCellIndex) -> Option<&mut CodepointInfo> {
        if index < self.nb_cells {
            self.get_mut_cpi(index)
        } else {
            None
        }
    }

    pub fn get_used_cpi_clipped(
        &self,
        index: LineCellIndex,
    ) -> (Option<&CodepointInfo>, LineCellIndex) {
        if self.nb_cells == 0 {
            return (None, 0);
        }

        let index = ::std::cmp::min(index, self.nb_cells - 1);
        (self.get_used_cpi(index), index)
    }
}
<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {

	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        let new_cpu_state = self.execute(cpu_state,mem);
        // Perform logging, recording here...
        *cpu_state = new_cpu_state
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);
        //cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
        cpu_state.pc = cpu_state.pc+1;
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState, mut mem:&Memory) -> CpuState {

    	// The state of the CPU after executing the current instruction.
    	let mut new_cpu_state = cpu_state.clone();

    	// Figure out which opcode is being executed.
    	let ref decode_register = cpu_state.decode_register;

    	match decode_register.info.opcode_class {

    		// ASL (Arithmetic Shift Left)
    		OpcodeClass::ASL => {

				// Shift accumulator to the left.
				new_cpu_state.a = cpu_state.a << 1;
				// Carry flag gets set to the most significant bit.
				new_cpu_state.C = (128 & cpu_state.a) > 0;
				// Set zero flag if result is 0.
    			if new_cpu_state.a == 0 { new_cpu_state.Z = true }

    		},

    		// LDA (Load Accumulator)
    		OpcodeClass::LDA => {
    			new_cpu_state.a = decode_register.value_final.unwrap()
    		},

    		// LDX (Load X)
    		OpcodeClass::LDX => {
    			new_cpu_state.x = decode_register.value_final.unwrap()
    		},

    		// LDY (Load Y)
    		OpcodeClass::LDY => {
    			new_cpu_state.y = decode_register.value_final.unwrap()
    		},

    		// LSR (Logical Shift Right)
    		OpcodeClass::LSR => {

    			// Shift accumulator to the right.
    			new_cpu_state.a = cpu_state.a >> 1;
    			// Carry flag gets set to the least significant bit.
    			new_cpu_state.C = 1 & cpu_state.a > 0;
    			// Set zero flag if result is 0.
    			if new_cpu_state.a == 0 { new_cpu_state.Z = true }

    		},

    		// NOP (No Operation)
    		OpcodeClass::NOP => {},

    		// Default: not sure what this opcode is.
			_ => panic!("Unrecognised opcode class: {:?}", decode_register.info.opcode_class)

    	}

    	// Expression returned is the updated cpu_state.
    	new_cpu_state
    }

}

<commit_msg>added power_on function<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {

	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

	/// Reset the given cpu_state.
    pub fn power_on(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        self.reset(cpu_state,mem);
    }
	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        let new_cpu_state = self.execute(cpu_state,mem);
        // Perform logging, recording here...
        *cpu_state = new_cpu_state
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);
        //cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
        cpu_state.pc = cpu_state.pc+1;
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState, mut mem:&Memory) -> CpuState {

    	// The state of the CPU after executing the current instruction.
    	let mut new_cpu_state = cpu_state.clone();

    	// Figure out which opcode is being executed.
    	let ref decode_register = cpu_state.decode_register;

    	match decode_register.info.opcode_class {

    		// ASL (Arithmetic Shift Left)
    		OpcodeClass::ASL => {

				// Shift accumulator to the left.
				new_cpu_state.a = cpu_state.a << 1;
				// Carry flag gets set to the most significant bit.
				new_cpu_state.C = (128 & cpu_state.a) > 0;
				// Set zero flag if result is 0.
    			if new_cpu_state.a == 0 { new_cpu_state.Z = true }

    		},

    		// LDA (Load Accumulator)
    		OpcodeClass::LDA => {
    			new_cpu_state.a = decode_register.value_final.unwrap()
    		},

    		// LDX (Load X)
    		OpcodeClass::LDX => {
    			new_cpu_state.x = decode_register.value_final.unwrap()
    		},

    		// LDY (Load Y)
    		OpcodeClass::LDY => {
    			new_cpu_state.y = decode_register.value_final.unwrap()
    		},

    		// LSR (Logical Shift Right)
    		OpcodeClass::LSR => {

    			// Shift accumulator to the right.
    			new_cpu_state.a = cpu_state.a >> 1;
    			// Carry flag gets set to the least significant bit.
    			new_cpu_state.C = 1 & cpu_state.a > 0;
    			// Set zero flag if result is 0.
    			if new_cpu_state.a == 0 { new_cpu_state.Z = true }

    		},

    		// NOP (No Operation)
    		OpcodeClass::NOP => {},

    		// Default: not sure what this opcode is.
			_ => panic!("Unrecognised opcode class: {:?}", decode_register.info.opcode_class)

    	}

    	// Expression returned is the updated cpu_state.
    	new_cpu_state
    }

}

<|endoftext|>


<commit_before>//! A simple adaptive threadpool that returns a oneshot future.

use std::{
    collections::VecDeque,
    sync::atomic::{AtomicBool, Ordering::Relaxed},
    thread,
    time::{Duration, Instant},
};

use parking_lot::{Condvar, Mutex};

use crate::{
    debug_delay, warn, Acquire, AtomicUsize, Error, Lazy, OneShot, Result,
    SeqCst,
};

// This is lower for CI reasons.
#[cfg(windows)]
const MAX_THREADS: usize = 16;

#[cfg(not(windows))]
const MAX_THREADS: usize = 128;

const DESIRED_WAITING_THREADS: usize = 7;

static WAITING_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);
static TOTAL_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);
static SPAWNS: AtomicUsize = AtomicUsize::new(0);
static SPAWNING: AtomicBool = AtomicBool::new(false);

type Work = Box<dyn FnOnce() + Send + 'static>;

struct Queue {
    cv: Condvar,
    mu: Mutex<VecDeque<Work>>,
}

impl Queue {
    fn recv_timeout(&self, duration: Duration) -> Option<Work> {
        let mut queue = self.mu.lock();

        let cutoff = Instant::now() + duration;

        while queue.is_empty() {
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            let res = self.cv.wait_until(&mut queue, cutoff);
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            if res.timed_out() {
                break;
            }
        }

        queue.pop_front()
    }

    fn try_recv(&self) -> Option<Work> {
        let mut queue = self.mu.lock();
        queue.pop_front()
    }

    fn send(&self, work: Work) -> usize {
        let mut queue = self.mu.lock();
        queue.push_back(work);

        let len = queue.len();

        // having held the mutex makes this linearized
        // with the notify below.
        drop(queue);

        self.cv.notify_all();

        len
    }
}

static QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(init_queue);
static BROKEN: AtomicBool = AtomicBool::new(false);

fn init_queue() -> Queue {
    debug_delay();
    for _ in 0..DESIRED_WAITING_THREADS {
        debug_delay();
        if let Err(e) = spawn_new_thread(true) {
            log::error!("failed to initialize threadpool: {:?}", e);
        }
    }
    Queue { cv: Condvar::new(), mu: Mutex::new(VecDeque::new()) }
}

fn perform_work(is_immortal: bool) {
    let wait_limit = Duration::from_secs(1);

    let mut performed = 0;
    let mut contiguous_overshoots = 0;

    while is_immortal || performed < 5 || contiguous_overshoots < 3 {
        debug_delay();
        let task_res = QUEUE.recv_timeout(wait_limit);

        if let Some(task) = task_res {
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            (task)();
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            performed += 1;
        }

        while let Some(task) = QUEUE.try_recv() {
            debug_delay();
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            (task)();
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            performed += 1;
        }

        debug_delay();

        let waiting = WAITING_THREAD_COUNT.load(Acquire);

        if waiting > DESIRED_WAITING_THREADS {
            contiguous_overshoots += 1;
        } else {
            contiguous_overshoots = 0;
        }
    }
}

// Create up to MAX_THREADS dynamic blocking task worker threads.
// Dynamic threads will terminate themselves if they don't
// receive any work after one second.
fn maybe_spawn_new_thread() -> Result<()> {
    debug_delay();
    let total_workers = TOTAL_THREAD_COUNT.load(Acquire);
    debug_delay();
    let waiting_threads = WAITING_THREAD_COUNT.load(Acquire);

    if waiting_threads >= DESIRED_WAITING_THREADS
        || total_workers >= MAX_THREADS
    {
        return Ok(());
    }

    if !SPAWNING.compare_and_swap(false, true, SeqCst) {
        spawn_new_thread(false)?;
    }

    Ok(())
}

fn spawn_new_thread(is_immortal: bool) -> Result<()> {
    if BROKEN.load(Relaxed) {
        return Err(Error::ReportableBug(
            "IO thread unexpectedly panicked. please report \
            this bug on the sled github repo."
                .to_string(),
        ));
    }

    let spawn_id = SPAWNS.fetch_add(1, SeqCst);

    TOTAL_THREAD_COUNT.fetch_add(1, SeqCst);
    let spawn_res = thread::Builder::new()
        .name(format!("sled-io-{}", spawn_id))
        .spawn(move || {
            SPAWNING.store(false, SeqCst);
            debug_delay();
            let res = std::panic::catch_unwind(|| perform_work(is_immortal));
            TOTAL_THREAD_COUNT.fetch_sub(1, SeqCst);
            if is_immortal || res.is_err() {
                // IO thread panicked, shut down the system
                log::error!(
                    "IO thread unexpectedly terminated.
                    please report this error at the sled github repo. {:?}",
                    res
                );
                BROKEN.store(true, SeqCst);
            }
        });

    if let Err(e) = spawn_res {
        static E: AtomicBool = AtomicBool::new(false);

        SPAWNING.store(false, SeqCst);

        if !E.compare_and_swap(false, true, Relaxed) {
            // only execute this once
            warn!(
                "Failed to dynamically increase the threadpool size: {:?}.",
                e,
            )
        }
    }

    Ok(())
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    let (promise_filler, promise) = OneShot::pair();
    let task = move || {
        let result = (work)();
        promise_filler.fill(result);
    };

    let depth = QUEUE.send(Box::new(task));

    if depth > DESIRED_WAITING_THREADS {
        maybe_spawn_new_thread()?;
    }

    Ok(promise)
}
<commit_msg>Add (buggy) drain function to threadpool so we can start running proper async tests<commit_after>//! A simple adaptive threadpool that returns a oneshot future.

use std::{
    collections::VecDeque,
    sync::atomic::{
        AtomicBool, AtomicU64,
        Ordering::{Acquire, Relaxed, Release, SeqCst},
    },
    thread,
    time::{Duration, Instant},
};

use parking_lot::{Condvar, Mutex};

use crate::{debug_delay, warn, AtomicUsize, Error, Lazy, OneShot, Result};

// This is lower for CI reasons.
#[cfg(windows)]
const MAX_THREADS: usize = 16;

#[cfg(not(windows))]
const MAX_THREADS: usize = 128;

const DESIRED_WAITING_THREADS: usize = 7;

static WAITING_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);
static TOTAL_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);
static SPAWNS: AtomicUsize = AtomicUsize::new(0);
static SPAWNING: AtomicBool = AtomicBool::new(false);
static SUBMITTED: AtomicU64 = AtomicU64::new(0);
static COMPLETED: AtomicU64 = AtomicU64::new(0);
static QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(init_queue);
static BROKEN: AtomicBool = AtomicBool::new(false);

type Work = Box<dyn FnOnce() + Send + 'static>;

fn init_queue() -> Queue {
    debug_delay();
    for _ in 0..DESIRED_WAITING_THREADS {
        debug_delay();
        if let Err(e) = spawn_new_thread(true) {
            log::error!("failed to initialize threadpool: {:?}", e);
        }
    }
    Queue { cv: Condvar::new(), mu: Mutex::new(VecDeque::new()) }
}

struct Queue {
    cv: Condvar,
    mu: Mutex<VecDeque<Work>>,
}

impl Queue {
    fn recv_timeout(&self, duration: Duration) -> Option<Work> {
        let mut queue = self.mu.lock();

        let cutoff = Instant::now() + duration;

        while queue.is_empty() {
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            let res = self.cv.wait_until(&mut queue, cutoff);
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            if res.timed_out() {
                break;
            }
        }

        queue.pop_front()
    }

    fn try_recv(&self) -> Option<Work> {
        let mut queue = self.mu.lock();
        queue.pop_front()
    }

    fn send(&self, work: Work) -> usize {
        let mut queue = self.mu.lock();
        queue.push_back(work);

        let len = queue.len();

        // having held the mutex makes this linearized
        // with the notify below.
        drop(queue);

        self.cv.notify_all();

        len
    }
}

fn perform_work(is_immortal: bool) -> Result<()> {
    let wait_limit = Duration::from_secs(1);

    let mut performed = 0;
    let mut contiguous_overshoots = 0;

    while is_immortal || performed < 5 || contiguous_overshoots < 3 {
        debug_delay();
        let task_res = QUEUE.recv_timeout(wait_limit);

        if let Some(task) = task_res {
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            (task)();
            COMPLETED.fetch_add(1, Release);
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            performed += 1;
        }

        while let Some(task) = QUEUE.try_recv() {
            debug_delay();
            WAITING_THREAD_COUNT.fetch_sub(1, SeqCst);
            (task)();
            COMPLETED.fetch_add(1, Release);
            WAITING_THREAD_COUNT.fetch_add(1, SeqCst);
            performed += 1;
        }

        debug_delay();

        let waiting = WAITING_THREAD_COUNT.load(Acquire);

        if waiting > DESIRED_WAITING_THREADS {
            contiguous_overshoots += 1;
        } else {
            contiguous_overshoots = 0;
        }
    }
    Ok(())
}

// Create up to MAX_THREADS dynamic blocking task worker threads.
// Dynamic threads will terminate themselves if they don't
// receive any work after one second.
fn maybe_spawn_new_thread() -> Result<()> {
    debug_delay();
    let total_workers = TOTAL_THREAD_COUNT.load(Acquire);
    debug_delay();
    let waiting_threads = WAITING_THREAD_COUNT.load(Acquire);

    if waiting_threads >= DESIRED_WAITING_THREADS
        || total_workers >= MAX_THREADS
    {
        return Ok(());
    }

    if !SPAWNING.compare_and_swap(false, true, SeqCst) {
        spawn_new_thread(false)?;
    }

    Ok(())
}

fn spawn_new_thread(is_immortal: bool) -> Result<()> {
    if BROKEN.load(Relaxed) {
        return Err(Error::ReportableBug(
            "IO thread unexpectedly panicked. please report \
            this bug on the sled github repo."
                .to_string(),
        ));
    }

    let spawn_id = SPAWNS.fetch_add(1, SeqCst);

    TOTAL_THREAD_COUNT.fetch_add(1, SeqCst);
    let spawn_res = thread::Builder::new()
        .name(format!("sled-io-{}", spawn_id))
        .spawn(move || {
            SPAWNING.store(false, SeqCst);
            debug_delay();
            let res = std::panic::catch_unwind(|| perform_work(is_immortal));
            TOTAL_THREAD_COUNT.fetch_sub(1, SeqCst);
            if is_immortal {
                // IO thread panicked, shut down the system
                BROKEN.store(true, SeqCst);
                panic!(
                    "IO thread unexpectedly panicked. please report \
                    this bug on the sled github repo. error: {:?}",
                    res
                );
            }
        });

    if let Err(e) = spawn_res {
        static E: AtomicBool = AtomicBool::new(false);

        SPAWNING.store(false, SeqCst);

        if !E.compare_and_swap(false, true, Relaxed) {
            // only execute this once
            warn!(
                "Failed to dynamically increase the threadpool size: {:?}.",
                e,
            )
        }
    }

    Ok(())
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    SUBMITTED.fetch_add(1, Acquire);
    let (promise_filler, promise) = OneShot::pair();
    let task = move || {
        promise_filler.fill((work)());
    };

    let depth = QUEUE.send(Box::new(task));

    if depth > DESIRED_WAITING_THREADS {
        maybe_spawn_new_thread()?;
    }

    Ok(promise)
}

/// Wait for all submitted tasks to complete
#[cfg(feature = "testing")]
pub fn drain() -> Result<()> {
    let submitted = SUBMITTED.load(Acquire);

    while COMPLETED.load(Acquire) < submitted {
        if BROKEN.load(Relaxed) {
            return Err(Error::ReportableBug(
                "IO thread unexpectedly panicked. please report \
            this bug on the sled github repo."
                    .to_string(),
            ));
        }
        std::thread::sleep(std::time::Duration::from_millis(10));
    }

    Ok(())
}
<|endoftext|>