<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Liberally derived from the [Firefox JS implementation]
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webbrowser.js).
//! Connection point for remote devtools that wish to investigate a particular Browsing Context's contents.
//! Supports dynamic attaching and detaching which control notifications of navigation, etc.

use actor::{Actor, ActorMessageStatus, ActorRegistry};
use actors::console::ConsoleActor;
use devtools_traits::DevtoolScriptControlMsg::{self, WantsLiveNotifications};
use protocol::JsonPacketStream;
use serde_json::{Map, Value};
use std::net::TcpStream;

#[derive(Serialize)]
struct BrowsingContextTraits;

#[derive(Serialize)]
struct BrowsingContextAttachedReply {
    from: String,
    #[serde(rename = "type")]
    type_: String,
    threadActor: String,
    cacheDisabled: bool,
    javascriptEnabled: bool,
    traits: BrowsingContextTraits,
}

#[derive(Serialize)]
struct BrowsingContextDetachedReply {
    from: String,
    #[serde(rename = "type")]
    type_: String,
}

#[derive(Serialize)]
struct ReconfigureReply {
    from: String,
}

#[derive(Serialize)]
struct ListFramesReply {
    from: String,
    frames: Vec<FrameMsg>,
}

#[derive(Serialize)]
struct FrameMsg {
    id: u32,
    url: String,
    title: String,
    parentID: u32,
}

#[derive(Serialize)]
struct ListWorkersReply {
    from: String,
    workers: Vec<WorkerMsg>,
}

#[derive(Serialize)]
struct WorkerMsg {
    id: u32,
    url: String,
    title: String,
    parentID: u32,
}

#[derive(Serialize)]
pub struct BrowsingContextActorMsg {
    actor: String,
    title: String,
    url: String,
    outerWindowID: u32,
    consoleActor: String,
    inspectorActor: String,
    timelineActor: String,
    profilerActor: String,
    performanceActor: String,
}

pub struct BrowsingContextActor {
    pub name: String,
    pub title: String,
    pub url: String,
    pub console: String,
    pub inspector: String,
    pub timeline: String,
    pub profiler: String,
    pub performance: String,
    pub thread: String,
}

impl Actor for BrowsingContextActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(
        &self,
        registry: &ActorRegistry,
        msg_type: &str,
        msg: &Map<String, Value>,
        stream: &mut TcpStream,
    ) -> Result<ActorMessageStatus, ()> {
        Ok(match msg_type {
            "reconfigure" => {
                if let Some(options) = msg.get("options").and_then(|o| o.as_object()) {
                    if let Some(val) = options.get("performReload") {
                        if val.as_bool().unwrap_or(false) {
                            let console_actor = registry.find::<ConsoleActor>(&self.console);
                            let _ = console_actor
                                .script_chan
                                .send(DevtoolScriptControlMsg::Reload(console_actor.pipeline));
                        }
                    }
                }
                stream.write_json_packet(&ReconfigureReply { from: self.name() });
                ActorMessageStatus::Processed
            },

            // https://docs.firefox-dev.tools/backend/protocol.html#listing-browser-tabs
            // (see "To attach to a _targetActor_")
            "attach" => {
                let msg = BrowsingContextAttachedReply {
                    from: self.name(),
                    type_: "targetAttached".to_owned(),
                    threadActor: self.thread.clone(),
                    cacheDisabled: false,
                    javascriptEnabled: true,
                    traits: BrowsingContextTraits,
                };
                let console_actor = registry.find::<ConsoleActor>(&self.console);
                console_actor
                    .streams
                    .borrow_mut()
                    .push(stream.try_clone().unwrap());
                stream.write_json_packet(&msg);
                console_actor
                    .script_chan
                    .send(WantsLiveNotifications(console_actor.pipeline, true))
                    .unwrap();
                ActorMessageStatus::Processed
            },

            //FIXME: The current implementation won't work for multiple connections. Need to ensure 105
            //       that the correct stream is removed.
            "detach" => {
                let msg = BrowsingContextDetachedReply {
                    from: self.name(),
                    type_: "detached".to_owned(),
                };
                let console_actor = registry.find::<ConsoleActor>(&self.console);
                console_actor.streams.borrow_mut().pop();
                stream.write_json_packet(&msg);
                console_actor
                    .script_chan
                    .send(WantsLiveNotifications(console_actor.pipeline, false))
                    .unwrap();
                ActorMessageStatus::Processed
            },

            "listFrames" => {
                let msg = ListFramesReply {
                    from: self.name(),
                    frames: vec![],
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "listWorkers" => {
                let msg = ListWorkersReply {
                    from: self.name(),
                    workers: vec![],
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            _ => ActorMessageStatus::Ignored,
        })
    }
}

impl BrowsingContextActor {
    pub fn encodable(&self) -> BrowsingContextActorMsg {
        BrowsingContextActorMsg {
            actor: self.name(),
            title: self.title.clone(),
            url: self.url.clone(),
            outerWindowID: 0, //FIXME: this should probably be the pipeline id
            consoleActor: self.console.clone(),
            inspectorActor: self.inspector.clone(),
            timelineActor: self.timeline.clone(),
            profilerActor: self.profiler.clone(),
            performanceActor: self.performance.clone(),
        }
    }
}
<commit_msg>remove any fields that are not totally necessary<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Liberally derived from the [Firefox JS implementation]
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webbrowser.js).
//! Connection point for remote devtools that wish to investigate a particular Browsing Context's contents.
//! Supports dynamic attaching and detaching which control notifications of navigation, etc.

use actor::{Actor, ActorMessageStatus, ActorRegistry};
use actors::console::ConsoleActor;
use devtools_traits::DevtoolScriptControlMsg::{self, WantsLiveNotifications};
use protocol::JsonPacketStream;
use serde_json::{Map, Value};
use std::net::TcpStream;

#[derive(Serialize)]
struct BrowsingContextTraits;

#[derive(Serialize)]
struct BrowsingContextAttachedReply {
    from: String,
    #[serde(rename = "type")]
    type_: String,
    threadActor: String,
    cacheDisabled: bool,
    javascriptEnabled: bool,
    traits: BrowsingContextTraits,
}

#[derive(Serialize)]
struct BrowsingContextDetachedReply {
    from: String,
    #[serde(rename = "type")]
    type_: String,
}

#[derive(Serialize)]
struct ReconfigureReply {
    from: String,
}

#[derive(Serialize)]
struct ListFramesReply {
    from: String,
    frames: Vec<FrameMsg>,
}

#[derive(Serialize)]
struct FrameMsg {
    id: u32,
    url: String,
    title: String,
    parentID: u32,
}

#[derive(Serialize)]
struct ListWorkersReply {
    from: String,
    workers: Vec<WorkerMsg>,
}

#[derive(Serialize)]
struct WorkerMsg {
    id: u32,
}

#[derive(Serialize)]
pub struct BrowsingContextActorMsg {
    actor: String,
    title: String,
    url: String,
    outerWindowID: u32,
    consoleActor: String,
    inspectorActor: String,
    timelineActor: String,
    profilerActor: String,
    performanceActor: String,
}

pub struct BrowsingContextActor {
    pub name: String,
    pub title: String,
    pub url: String,
    pub console: String,
    pub inspector: String,
    pub timeline: String,
    pub profiler: String,
    pub performance: String,
    pub thread: String,
}

impl Actor for BrowsingContextActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(
        &self,
        registry: &ActorRegistry,
        msg_type: &str,
        msg: &Map<String, Value>,
        stream: &mut TcpStream,
    ) -> Result<ActorMessageStatus, ()> {
        Ok(match msg_type {
            "reconfigure" => {
                if let Some(options) = msg.get("options").and_then(|o| o.as_object()) {
                    if let Some(val) = options.get("performReload") {
                        if val.as_bool().unwrap_or(false) {
                            let console_actor = registry.find::<ConsoleActor>(&self.console);
                            let _ = console_actor
                                .script_chan
                                .send(DevtoolScriptControlMsg::Reload(console_actor.pipeline));
                        }
                    }
                }
                stream.write_json_packet(&ReconfigureReply { from: self.name() });
                ActorMessageStatus::Processed
            },

            // https://docs.firefox-dev.tools/backend/protocol.html#listing-browser-tabs
            // (see "To attach to a _targetActor_")
            "attach" => {
                let msg = BrowsingContextAttachedReply {
                    from: self.name(),
                    type_: "targetAttached".to_owned(),
                    threadActor: self.thread.clone(),
                    cacheDisabled: false,
                    javascriptEnabled: true,
                    traits: BrowsingContextTraits,
                };
                let console_actor = registry.find::<ConsoleActor>(&self.console);
                console_actor
                    .streams
                    .borrow_mut()
                    .push(stream.try_clone().unwrap());
                stream.write_json_packet(&msg);
                console_actor
                    .script_chan
                    .send(WantsLiveNotifications(console_actor.pipeline, true))
                    .unwrap();
                ActorMessageStatus::Processed
            },

            //FIXME: The current implementation won't work for multiple connections. Need to ensure 105
            //       that the correct stream is removed.
            "detach" => {
                let msg = BrowsingContextDetachedReply {
                    from: self.name(),
                    type_: "detached".to_owned(),
                };
                let console_actor = registry.find::<ConsoleActor>(&self.console);
                console_actor.streams.borrow_mut().pop();
                stream.write_json_packet(&msg);
                console_actor
                    .script_chan
                    .send(WantsLiveNotifications(console_actor.pipeline, false))
                    .unwrap();
                ActorMessageStatus::Processed
            },

            "listFrames" => {
                let msg = ListFramesReply {
                    from: self.name(),
                    frames: vec![],
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "listWorkers" => {
                let msg = ListWorkersReply {
                    from: self.name(),
                    workers: vec![],
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            _ => ActorMessageStatus::Ignored,
        })
    }
}

impl BrowsingContextActor {
    pub fn encodable(&self) -> BrowsingContextActorMsg {
        BrowsingContextActorMsg {
            actor: self.name(),
            title: self.title.clone(),
            url: self.url.clone(),
            outerWindowID: 0, //FIXME: this should probably be the pipeline id
            consoleActor: self.console.clone(),
            inspectorActor: self.inspector.clone(),
            timelineActor: self.timeline.clone(),
            profilerActor: self.profiler.clone(),
            performanceActor: self.performance.clone(),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cmp::{Eq, TotalEq};

pub enum IpAddr {
    Ipv4(u8, u8, u8, u8, u16),
    Ipv6
}

impl Eq for IpAddr {
    fn eq(&self, other: &IpAddr) -> bool {
        match (*self, *other) {
            (Ipv4(a,b,c,d,e), Ipv4(f,g,h,i,j)) => (a,b,c,d,e) == (f,g,h,i,j),
            (Ipv6, Ipv6) => fail!(),
            _ => false
        }
    }
    fn ne(&self, other: &IpAddr) -> bool {
        !(self == other)
    }
}

impl TotalEq for IpAddr {
    fn equals(&self, other: &IpAddr) -> bool {
        *self == *other
    }
}
<commit_msg>derived instances of Eq and TotalEq for IpAddr rather than implement them manually.<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

#[deriving(Eq, TotalEq)]
pub enum IpAddr {
    Ipv4(u8, u8, u8, u8, u16),
    Ipv6
}
<|endoftext|>


<commit_before>
<commit_msg>Delte logger.rs<commit_after><|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate serde_json;

use foxbox_taxonomy::manager::WatchGuard;
use foxbox_taxonomy::api::{ API, TargetMap };
use foxbox_taxonomy::values::Value;
use foxbox_taxonomy::selector::*;
use foxbox_taxonomy::services::*;

use foxbox_users::AuthEndpoint;

use iron::{ Handler, IronResult, Request, Response };
use iron::headers::ContentType;
use iron::method::Method;
use iron::prelude::Chain;
use iron::request::Body;
use iron::status::Status;

use std::io::{ Error as IOError, Read };
use std::sync::Mutex;
use traits::Controller;

/// This is a specialized Router for the taxonomy API.
/// It handles all the calls under the api/v1/ url space.
pub struct TaxonomyRouter {
    api: Mutex
}

impl TaxonomyRouter
    where A: API<WatchGuard=WatchGuard> + 'static {
    pub fn new(adapter_api: A) -> Self {
        TaxonomyRouter {
            // This locks the full api access when dealing with a http request,
            // which basically kills http concurrency.
            // TODO: make it concurrent again.
            api: Mutex::new(adapter_api)
        }
    }

    fn build_response<S: ToJSON>(&self, obj: &S) -> IronResult<Response> {
        let json = obj.to_json();
        let serialized = itry!(serde_json::to_string(&json));
        let mut response = Response::with(serialized);
        response.status = Some(Status::Ok);
        response.headers.set(ContentType::json());
        Ok(response)
    }

    fn read_body_to_string<'a, 'b : 'a>(body: &mut Body<'a, 'b>) -> Result<String, IOError> {
        let mut s = String::new();
        try!(body.read_to_string(&mut s));
        Ok(s)
    }
}

impl Handler for TaxonomyRouter
    where A: API<WatchGuard=WatchGuard> + 'static {

    #[allow(cyclomatic_complexity)]
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // We are handled urls relative to the mounter set up in http_server.rs
        // That means that for a full url like http://localhost/api/v1/services
        // the req.url.path will only contain ["services"]
        let path = req.url.path.clone();

        /// Generates the code for a generic HTTP call, where we use an empty
        /// taxonomy selector for GET requests, and a decoded json body for POST ones.
        /// $call is the method we'll call on the api, like get_services.
        /// $sel  is the selector type, like ServiceSelector
        /// $path is a vector describing the url path, like ["service", "tags"]
        macro_rules! get_post_api {
            ($call:ident, $sel:ident, $path:expr) => (
            if path == $path {
                return {
                    let api = self.api.lock().unwrap();

                    if req.method != Method::Get && req.method != Method::Post {
                        // Wrong method, return a 405 error.
                        return Ok(Response::with((Status::MethodNotAllowed,
                                                  format!("Bad method: {}", req.method))));
                    }

                    let result = if req.method == Method::Get {
                        // On a GET, just send the full taxonomy content for
                        // this kind of selector.
                        api.$call(vec![$sel::new()])
                    } else {
                        let source = itry!(Self::read_body_to_string(&mut req.body));
                        let selectors = itry!(Vec::<$sel>::from_str(&source));
                        api.$call(selectors)
                    };

                    self.build_response(&result)
                }
            })
        }

        // Generates the code to process a given HTTP call with a json body.
        macro_rules! payload_api {
            ($call:ident, $param:ty, $path:expr, $method:expr) => (
                if path == $path && req.method == $method {
                    type Selectors = $param;
                    return {
                        let api = self.api.lock().unwrap();
                        let source = itry!(Self::read_body_to_string(&mut req.body));
                        let selectors = itry!(Selectors::from_str(&source));
                        let result = api.$call(selectors);
                        return self.build_response(&result);
                    }
                }
            )
        }

        // Generates the code to process a given HTTP call with a json body.
        // This version takes 2 parameters for the internal call.
        macro_rules! payload_api2 {
            ($call:ident, $name1:ident => $param1:ty, $name2:ident => $param2:ty, $path:expr, $method:expr) => (
                if path == $path && req.method == $method {
                    type Param1 = $param1;
                    type Param2 = $param2;
                    return {
                        let api = self.api.lock().unwrap();

                        let source = itry!(Self::read_body_to_string(&mut req.body));
                        let mut args: JSON = itry!(serde_json::de::from_str(&source));
                        let arg_1 = itry!(Param1::take(Path::new(), &mut args, stringify!($name1)));
                        let arg_2 = itry!(Param2::take(Path::new(), &mut args, stringify!($name2)));

                        let result = api.$call(arg_1, arg_2);
                        return self.build_response(&result);
                    }
                }
            )
        }

        // Keep these urls in sync with the AuthEndpoint(s) in the create() method.

        // Selectors queries.
        get_post_api!(get_services, ServiceSelector, ["services"]);
        get_post_api!(get_getter_channels, GetterSelector, ["channels", "getters"]);
        get_post_api!(get_setter_channels, SetterSelector, ["channels", "setters"]);

        // Fetching and getting values.
        payload_api!(fetch_values, Vec<GetterSelector>, ["channels", "get"], Method::Get);
        payload_api!(send_values, TargetMap<SetterSelector, Value>, ["channels", "set"], Method::Put);

        // Adding tags.
        payload_api2!(add_service_tags,
                      services => Vec<ServiceSelector>,
                      tags => Vec<Id<TagId>>,
                      ["services", "tags"], Method::Post);
        payload_api2!(add_getter_tags,
                      getters => Vec<GetterSelector>,
                      tags => Vec<Id<TagId>>,
                      ["channels", "getter", "tags"], Method::Post);
        payload_api2!(add_setter_tags,
                      setters => Vec<SetterSelector>,
                      tags => Vec<Id<TagId>>,
                      ["channels", "getter", "tags"], Method::Post);

        // Removing tags.
        payload_api2!(remove_service_tags,
                      services => Vec<ServiceSelector>,
                      tags => Vec<Id<TagId>>,
                      ["services", "tags"], Method::Delete);
        payload_api2!(remove_getter_tags,
                      getters => Vec<GetterSelector>,
                      tags => Vec<Id<TagId>>,
                      ["channels", "getter", "tags"], Method::Delete);
        payload_api2!(remove_setter_tags,
                      setters => Vec<SetterSelector>,
                      tags => Vec<Id<TagId>>,
                      ["channels", "getter", "tags"], Method::Delete);

        // Fallthrough, returning a 404.
        Ok(Response::with((Status::NotFound,
                           format!("Unknown url: {}", req.url))))
    }
}

pub fn create<T, A>(controller: T, adapter_api: A) -> Chain
    where A: API<WatchGuard=WatchGuard> + 'static,
          T: Controller {
    let router = TaxonomyRouter::new(adapter_api);

    let auth_endpoints = if cfg!(feature = "authentication") && !cfg!(test) {
        // Keep this list in sync with all the (url path, http method) from
        // the handle() method and with the CORS chain in http_server.rs
        vec![
            AuthEndpoint(vec![Method::Get, Method::Post], "services".to_owned()),
            AuthEndpoint(vec![Method::Post, Method::Delete], "services/tags".to_owned()),
            AuthEndpoint(vec![Method::Get, Method::Post], "channels/getters".to_owned()),
            AuthEndpoint(vec![Method::Get, Method::Post], "channels/setters".to_owned()),
            AuthEndpoint(vec![Method::Get], "channels/get".to_owned()),
            AuthEndpoint(vec![Method::Put], "channels/set".to_owned()),
            AuthEndpoint(vec![Method::Post, Method::Delete], "channel/getters/tags".to_owned()),
            AuthEndpoint(vec![Method::Post, Method::Delete], "channel/setters/tags".to_owned())
        ]
    } else {
        vec![]
    };

    let mut chain = Chain::new(router);
    chain.around(controller.get_users_manager().get_middleware(auth_endpoints));

    chain
}

/* // FIXME: Deactivated while we change the JSON format. Need to reactivate this later.
#[cfg(test)]
describe! taxonomy_router {
    before_each {
        extern crate serde_json;

        use adapters::clock;
        use foxbox_taxonomy::manager::AdapterManager;
        use foxbox_taxonomy::services::Service;
        use iron::Headers;
        use iron_test::{ request, response };
        use mount::Mount;
        use stubs::controller::ControllerStub;

        // Custom comparison function for services, since the json serialization
        // is not stable, and we don't want to expose PartialEq on Service in
        // general.
        fn service_equals(service1: &Service, service2: &Service) -> bool {
            service1.id == service2.id &&
            service1.adapter == service2.adapter &&
            service1.tags == service2.tags &&
            service1.properties == service2.properties &&
            service1.getters == service2.getters &&
            service1.setters == service2.setters
        }

        let taxo_manager = AdapterManager::new();
        clock::Clock::init(&taxo_manager).unwrap();

        let mut mount = Mount::new();
        mount.mount("/api/v1", create(ControllerStub::new(), taxo_manager));
    }

    it "should return the list of services from a GET request" {
        let response = request::get("http://localhost:3000/api/v1/services",
                                    Headers::new(),
                                    &mount).unwrap();
        let body = response::extract_body_to_string(response);
        let observed: Vec<Service> = serde_json::from_str(&body).unwrap();

        let s = r#"[{"tags":[],"properties":{"model":"Mozilla clock v1"},"id":"service:clock@link.mozilla.org","getters":{"getter:timestamp.clock@link.mozilla.org":{"tags":[],"id":"getter:timestamp.clock@link.mozilla.org","service":"service:clock@link.mozilla.org","mechanism":{"kind":{"CurrentTime":[]},"updated":null},"adapter":"clock@link.mozilla.org","last_seen":null},"getter:timeofday.clock@link.mozilla.org":{"tags":[],"id":"getter:timeofday.clock@link.mozilla.org","service":"service:clock@link.mozilla.org","mechanism":{"kind":{"CurrentTimeOfDay":[]},"updated":null},"adapter":"clock@link.mozilla.org","last_seen":null}},"setters":{},"adapter":"clock@link.mozilla.org"}]"#;
        let expected: Vec<Service> = serde_json::from_str(&s).unwrap();

        // We only registered the clock service.
        assert_eq!(observed.len(), 1);
        assert!(service_equals(&observed[0], &expected[0]));
    }

    it "should return the list of services from a POST request" {
        let response = request::post("http://localhost:3000/api/v1/services",
                                    Headers::new(),
                                    r#"[{"id":"service:clock@link.mozilla.org"}]"#,
                                    &mount).unwrap();
        let body = response::extract_body_to_string(response);
        let observed: Vec<Service> = serde_json::from_str(&body).unwrap();

        let s = r#"[{"tags":[],"properties":{"model":"Mozilla clock v1"},"id":"service:clock@link.mozilla.org","getters":{"getter:timestamp.clock@link.mozilla.org":{"tags":[],"id":"getter:timestamp.clock@link.mozilla.org","service":"service:clock@link.mozilla.org","mechanism":{"kind":{"CurrentTime":[]},"updated":null},"adapter":"clock@link.mozilla.org","last_seen":null},"getter:timeofday.clock@link.mozilla.org":{"tags":[],"id":"getter:timeofday.clock@link.mozilla.org","service":"service:clock@link.mozilla.org","mechanism":{"kind":{"CurrentTimeOfDay":[]},"updated":null},"adapter":"clock@link.mozilla.org","last_seen":null}},"setters":{},"adapter":"clock@link.mozilla.org"}]"#;
        let expected: Vec<Service> = serde_json::from_str(&s).unwrap();

        // We only registered the clock service.
        assert_eq!(observed.len(), 1);
        assert!(service_equals(&observed[0], &expected[0]));
    }
}
*/
<commit_msg>Re-enable taxonomy router tests<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate serde_json;

use foxbox_taxonomy::manager::WatchGuard;
use foxbox_taxonomy::api::{ API, TargetMap };
use foxbox_taxonomy::values::Value;
use foxbox_taxonomy::selector::*;
use foxbox_taxonomy::services::*;

use foxbox_users::AuthEndpoint;

use iron::{ Handler, IronResult, Request, Response };
use iron::headers::ContentType;
use iron::method::Method;
use iron::prelude::Chain;
use iron::request::Body;
use iron::status::Status;

use std::io::{ Error as IOError, Read };
use std::sync::Mutex;
use traits::Controller;

/// This is a specialized Router for the taxonomy API.
/// It handles all the calls under the api/v1/ url space.
pub struct TaxonomyRouter {
    api: Mutex
}

impl TaxonomyRouter
    where A: API<WatchGuard=WatchGuard> + 'static {
    pub fn new(adapter_api: A) -> Self {
        TaxonomyRouter {
            // This locks the full api access when dealing with a http request,
            // which basically kills http concurrency.
            // TODO: make it concurrent again.
            api: Mutex::new(adapter_api)
        }
    }

    fn build_response<S: ToJSON>(&self, obj: &S) -> IronResult<Response> {
        let json = obj.to_json();
        let serialized = itry!(serde_json::to_string(&json));
        let mut response = Response::with(serialized);
        response.status = Some(Status::Ok);
        response.headers.set(ContentType::json());
        Ok(response)
    }

    fn read_body_to_string<'a, 'b : 'a>(body: &mut Body<'a, 'b>) -> Result<String, IOError> {
        let mut s = String::new();
        try!(body.read_to_string(&mut s));
        Ok(s)
    }
}

impl Handler for TaxonomyRouter
    where A: API<WatchGuard=WatchGuard> + 'static {

    #[allow(cyclomatic_complexity)]
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // We are handled urls relative to the mounter set up in http_server.rs
        // That means that for a full url like http://localhost/api/v1/services
        // the req.url.path will only contain ["services"]
        let path = req.url.path.clone();

        /// Generates the code for a generic HTTP call, where we use an empty
        /// taxonomy selector for GET requests, and a decoded json body for POST ones.
        /// $call is the method we'll call on the api, like get_services.
        /// $sel  is the selector type, like ServiceSelector
        /// $path is a vector describing the url path, like ["service", "tags"]
        macro_rules! get_post_api {
            ($call:ident, $sel:ident, $path:expr) => (
            if path == $path {
                return {
                    let api = self.api.lock().unwrap();

                    if req.method != Method::Get && req.method != Method::Post {
                        // Wrong method, return a 405 error.
                        return Ok(Response::with((Status::MethodNotAllowed,
                                                  format!("Bad method: {}", req.method))));
                    }

                    let result = if req.method == Method::Get {
                        // On a GET, just send the full taxonomy content for
                        // this kind of selector.
                        api.$call(vec![$sel::new()])
                    } else {
                        let source = itry!(Self::read_body_to_string(&mut req.body));
                        let selectors = itry!(Vec::<$sel>::from_str(&source));
                        api.$call(selectors)
                    };

                    self.build_response(&result)
                }
            })
        }

        // Generates the code to process a given HTTP call with a json body.
        macro_rules! payload_api {
            ($call:ident, $param:ty, $path:expr, $method:expr) => (
                if path == $path && req.method == $method {
                    type Selectors = $param;
                    return {
                        let api = self.api.lock().unwrap();
                        let source = itry!(Self::read_body_to_string(&mut req.body));
                        let selectors = itry!(Selectors::from_str(&source));
                        let result = api.$call(selectors);
                        return self.build_response(&result);
                    }
                }
            )
        }

        // Generates the code to process a given HTTP call with a json body.
        // This version takes 2 parameters for the internal call.
        macro_rules! payload_api2 {
            ($call:ident, $name1:ident => $param1:ty, $name2:ident => $param2:ty, $path:expr, $method:expr) => (
                if path == $path && req.method == $method {
                    type Param1 = $param1;
                    type Param2 = $param2;
                    return {
                        let api = self.api.lock().unwrap();

                        let source = itry!(Self::read_body_to_string(&mut req.body));
                        let mut args: JSON = itry!(serde_json::de::from_str(&source));
                        let arg_1 = itry!(Param1::take(Path::new(), &mut args, stringify!($name1)));
                        let arg_2 = itry!(Param2::take(Path::new(), &mut args, stringify!($name2)));

                        let result = api.$call(arg_1, arg_2);
                        return self.build_response(&result);
                    }
                }
            )
        }

        // Keep these urls in sync with the AuthEndpoint(s) in the create() method.

        // Selectors queries.
        get_post_api!(get_services, ServiceSelector, ["services"]);
        get_post_api!(get_getter_channels, GetterSelector, ["channels", "getters"]);
        get_post_api!(get_setter_channels, SetterSelector, ["channels", "setters"]);

        // Fetching and getting values.
        payload_api!(fetch_values, Vec<GetterSelector>, ["channels", "get"], Method::Get);
        payload_api!(send_values, TargetMap<SetterSelector, Value>, ["channels", "set"], Method::Put);

        // Adding tags.
        payload_api2!(add_service_tags,
                      services => Vec<ServiceSelector>,
                      tags => Vec<Id<TagId>>,
                      ["services", "tags"], Method::Post);
        payload_api2!(add_getter_tags,
                      getters => Vec<GetterSelector>,
                      tags => Vec<Id<TagId>>,
                      ["channels", "getter", "tags"], Method::Post);
        payload_api2!(add_setter_tags,
                      setters => Vec<SetterSelector>,
                      tags => Vec<Id<TagId>>,
                      ["channels", "getter", "tags"], Method::Post);

        // Removing tags.
        payload_api2!(remove_service_tags,
                      services => Vec<ServiceSelector>,
                      tags => Vec<Id<TagId>>,
                      ["services", "tags"], Method::Delete);
        payload_api2!(remove_getter_tags,
                      getters => Vec<GetterSelector>,
                      tags => Vec<Id<TagId>>,
                      ["channels", "getter", "tags"], Method::Delete);
        payload_api2!(remove_setter_tags,
                      setters => Vec<SetterSelector>,
                      tags => Vec<Id<TagId>>,
                      ["channels", "getter", "tags"], Method::Delete);

        // Fallthrough, returning a 404.
        Ok(Response::with((Status::NotFound,
                           format!("Unknown url: {}", req.url))))
    }
}

pub fn create<T, A>(controller: T, adapter_api: A) -> Chain
    where A: API<WatchGuard=WatchGuard> + 'static,
          T: Controller {
    let router = TaxonomyRouter::new(adapter_api);

    let auth_endpoints = if cfg!(feature = "authentication") && !cfg!(test) {
        // Keep this list in sync with all the (url path, http method) from
        // the handle() method and with the CORS chain in http_server.rs
        vec![
            AuthEndpoint(vec![Method::Get, Method::Post], "services".to_owned()),
            AuthEndpoint(vec![Method::Post, Method::Delete], "services/tags".to_owned()),
            AuthEndpoint(vec![Method::Get, Method::Post], "channels/getters".to_owned()),
            AuthEndpoint(vec![Method::Get, Method::Post], "channels/setters".to_owned()),
            AuthEndpoint(vec![Method::Get], "channels/get".to_owned()),
            AuthEndpoint(vec![Method::Put], "channels/set".to_owned()),
            AuthEndpoint(vec![Method::Post, Method::Delete], "channel/getters/tags".to_owned()),
            AuthEndpoint(vec![Method::Post, Method::Delete], "channel/setters/tags".to_owned())
        ]
    } else {
        vec![]
    };

    let mut chain = Chain::new(router);
    chain.around(controller.get_users_manager().get_middleware(auth_endpoints));

    chain
}

#[cfg(test)]
describe! taxonomy_router {
    before_each {
        extern crate serde_json;

        use adapters::clock;
        use foxbox_taxonomy::manager::AdapterManager;
        use iron::Headers;
        use iron_test::{ request, response };
        use mount::Mount;
        use stubs::controller::ControllerStub;

        let taxo_manager = AdapterManager::new();
        clock::Clock::init(&taxo_manager).unwrap();

        let mut mount = Mount::new();
        mount.mount("/api/v1", create(ControllerStub::new(), taxo_manager));
    }

    it "should return the list of services from a GET request" {
        let response = request::get("http://localhost:3000/api/v1/services",
                                    Headers::new(),
                                    &mount).unwrap();
        let body = response::extract_body_to_string(response);
        let s = r#"[{"adapter":"clock@link.mozilla.org","getters":{"getter:timeofday.clock@link.mozilla.org":{"adapter":"clock@link.mozilla.org","id":"getter:timeofday.clock@link.mozilla.org","kind":"CurrentTimeOfDay","mechanism":"getter","service":"service:clock@link.mozilla.org","tags":[]},"getter:timestamp.clock@link.mozilla.org":{"adapter":"clock@link.mozilla.org","id":"getter:timestamp.clock@link.mozilla.org","kind":"CurrentTime","mechanism":"getter","service":"service:clock@link.mozilla.org","tags":[]}},"id":"service:clock@link.mozilla.org","properties":{"model":"Mozilla clock v1"},"setters":{},"tags":[]}]"#;

        assert_eq!(body, s);
    }

    it "should return the list of services from a POST request" {
        let response = request::post("http://localhost:3000/api/v1/services",
                                    Headers::new(),
                                    r#"[{"id":"service:clock@link.mozilla.org"}]"#,
                                    &mount).unwrap();
        let body = response::extract_body_to_string(response);
        let s = r#"[{"adapter":"clock@link.mozilla.org","getters":{"getter:timeofday.clock@link.mozilla.org":{"adapter":"clock@link.mozilla.org","id":"getter:timeofday.clock@link.mozilla.org","kind":"CurrentTimeOfDay","mechanism":"getter","service":"service:clock@link.mozilla.org","tags":[]},"getter:timestamp.clock@link.mozilla.org":{"adapter":"clock@link.mozilla.org","id":"getter:timestamp.clock@link.mozilla.org","kind":"CurrentTime","mechanism":"getter","service":"service:clock@link.mozilla.org","tags":[]}},"id":"service:clock@link.mozilla.org","properties":{"model":"Mozilla clock v1"},"setters":{},"tags":[]}]"#;

        assert_eq!(body, s);
    }
}
<|endoftext|>


<commit_before>// see BPFCallingConv.td
use crate::abi::call::{ArgAbi, FnAbi};

fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
    if ret.layout.is_aggregate() || ret.layout.size.bits() > 64 {
        ret.make_indirect();
    } else {
        ret.extend_integer_width_to(64);
    }
}

fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
    if arg.layout.is_aggregate() || arg.layout.size.bits() > 64 {
        arg.make_indirect();
    } else {
        arg.extend_integer_width_to(64);
    }
}

pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
    if !fn_abi.ret.is_ignore() {
        classify_ret(&mut fn_abi.ret);
    }

    for arg in &mut fn_abi.args {
        if arg.is_ignore() {
            continue;
        }
        classify_arg(arg);
    }
}
<commit_msg>BPF: abi: extend args/ret to 32 bits<commit_after>// see https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/BPF/BPFCallingConv.td
use crate::abi::call::{ArgAbi, FnAbi};

fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
    if ret.layout.is_aggregate() || ret.layout.size.bits() > 64 {
        ret.make_indirect();
    } else {
        ret.extend_integer_width_to(32);
    }
}

fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
    if arg.layout.is_aggregate() || arg.layout.size.bits() > 64 {
        arg.make_indirect();
    } else {
        arg.extend_integer_width_to(32);
    }
}

pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
    if !fn_abi.ret.is_ignore() {
        classify_ret(&mut fn_abi.ret);
    }

    for arg in &mut fn_abi.args {
        if arg.is_ignore() {
            continue;
        }
        classify_arg(arg);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
use crate::dom::bindings::codegen::Bindings::XRRigidTransformBinding;
use crate::dom::bindings::codegen::Bindings::XRRigidTransformBinding::XRRigidTransformMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::dompointreadonly::DOMPointReadOnly;
use crate::dom::globalscope::GlobalScope;
use crate::dom::vrframedata::create_typed_array;
use crate::dom::window::Window;
use crate::dom::xrsession::ApiRigidTransform;
use dom_struct::dom_struct;
use euclid::{TypedRigidTransform3D, TypedRotation3D, TypedVector3D};
use js::jsapi::{Heap, JSContext, JSObject};
use std::ptr::NonNull;

#[dom_struct]
pub struct XRRigidTransform {
    reflector_: Reflector,
    position: MutNullableDom<DOMPointReadOnly>,
    orientation: MutNullableDom<DOMPointReadOnly>,
    #[ignore_malloc_size_of = "defined in euclid"]
    transform: ApiRigidTransform,
    inverse: MutNullableDom<XRRigidTransform>,
    #[ignore_malloc_size_of = "defined in mozjs"]
    matrix: Heap<*mut JSObject>,
}

impl XRRigidTransform {
    fn new_inherited(transform: ApiRigidTransform) -> XRRigidTransform {
        XRRigidTransform {
            reflector_: Reflector::new(),
            position: MutNullableDom::default(),
            orientation: MutNullableDom::default(),
            transform,
            inverse: MutNullableDom::default(),
            matrix: Heap::default(),
        }
    }

    pub fn new(global: &GlobalScope, transform: ApiRigidTransform) -> DomRoot<XRRigidTransform> {
        reflect_dom_object(
            Box::new(XRRigidTransform::new_inherited(transform)),
            global,
            XRRigidTransformBinding::Wrap,
        )
    }

    pub fn identity(window: &GlobalScope) -> DomRoot<XRRigidTransform> {
        let transform = TypedRigidTransform3D::identity();
        XRRigidTransform::new(window, transform)
    }

    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-xrrigidtransform
    pub fn Constructor(
        window: &Window,
        position: &DOMPointInit,
        orientation: &DOMPointInit,
    ) -> Fallible<DomRoot<Self>> {
        if position.w != 1.0 {
            return Err(Error::Type(format!(
                "XRRigidTransform must be constructed with a position that has a w value of of 1.0, not {}",
                position.w
            )));
        }

        let translate = TypedVector3D::new(position.x as f32, position.y as f32, position.z as f32);
        let rotate = TypedRotation3D::unit_quaternion(
            orientation.x as f32,
            orientation.y as f32,
            orientation.z as f32,
            orientation.w as f32,
        );
        let transform = TypedRigidTransform3D::new(rotate, translate);
        Ok(XRRigidTransform::new(&window.global(), transform))
    }
}

impl XRRigidTransformMethods for XRRigidTransform {
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-position
    fn Position(&self) -> DomRoot<DOMPointReadOnly> {
        self.position.or_init(|| {
            let t = &self.transform.translation;
            DOMPointReadOnly::new(&self.global(), t.x.into(), t.y.into(), t.z.into(), 1.0)
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-orientation
    fn Orientation(&self) -> DomRoot<DOMPointReadOnly> {
        self.orientation.or_init(|| {
            let r = &self.transform.rotation;
            DOMPointReadOnly::new(
                &self.global(),
                r.i.into(),
                r.j.into(),
                r.k.into(),
                r.r.into(),
            )
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-inverse
    fn Inverse(&self) -> DomRoot<XRRigidTransform> {
        self.inverse.or_init(|| {
            let transform = XRRigidTransform::new(&self.global(), self.transform.inverse());
            transform.inverse.set(Some(self));
            transform
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-matrix
    #[allow(unsafe_code)]
    unsafe fn Matrix(&self, _cx: *mut JSContext) -> NonNull<JSObject> {
        if self.matrix.get().is_null() {
            let cx = self.global().get_cx();
            // According to the spec all matrices are column-major,
            // however euclid uses row vectors so we use .to_row_major_array()
            let arr = self.transform.to_transform().to_row_major_array();
            create_typed_array(cx, &arr, &self.matrix);
        }
        NonNull::new(self.matrix.get()).unwrap()
    }
}

impl XRRigidTransform {
    /// https://immersive-web.github.io/webxr/#dom-xrpose-transform
    pub fn transform(&self) -> ApiRigidTransform {
        self.transform
    }
}
<commit_msg>Error on invalid rotation values in XRRigidTransform constructor<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
use crate::dom::bindings::codegen::Bindings::XRRigidTransformBinding;
use crate::dom::bindings::codegen::Bindings::XRRigidTransformBinding::XRRigidTransformMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::dompointreadonly::DOMPointReadOnly;
use crate::dom::globalscope::GlobalScope;
use crate::dom::vrframedata::create_typed_array;
use crate::dom::window::Window;
use crate::dom::xrsession::ApiRigidTransform;
use dom_struct::dom_struct;
use euclid::{TypedRigidTransform3D, TypedRotation3D, TypedVector3D};
use js::jsapi::{Heap, JSContext, JSObject};
use std::ptr::NonNull;

#[dom_struct]
pub struct XRRigidTransform {
    reflector_: Reflector,
    position: MutNullableDom<DOMPointReadOnly>,
    orientation: MutNullableDom<DOMPointReadOnly>,
    #[ignore_malloc_size_of = "defined in euclid"]
    transform: ApiRigidTransform,
    inverse: MutNullableDom<XRRigidTransform>,
    #[ignore_malloc_size_of = "defined in mozjs"]
    matrix: Heap<*mut JSObject>,
}

impl XRRigidTransform {
    fn new_inherited(transform: ApiRigidTransform) -> XRRigidTransform {
        XRRigidTransform {
            reflector_: Reflector::new(),
            position: MutNullableDom::default(),
            orientation: MutNullableDom::default(),
            transform,
            inverse: MutNullableDom::default(),
            matrix: Heap::default(),
        }
    }

    pub fn new(global: &GlobalScope, transform: ApiRigidTransform) -> DomRoot<XRRigidTransform> {
        reflect_dom_object(
            Box::new(XRRigidTransform::new_inherited(transform)),
            global,
            XRRigidTransformBinding::Wrap,
        )
    }

    pub fn identity(window: &GlobalScope) -> DomRoot<XRRigidTransform> {
        let transform = TypedRigidTransform3D::identity();
        XRRigidTransform::new(window, transform)
    }

    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-xrrigidtransform
    pub fn Constructor(
        window: &Window,
        position: &DOMPointInit,
        orientation: &DOMPointInit,
    ) -> Fallible<DomRoot<Self>> {
        if position.w != 1.0 {
            return Err(Error::Type(format!(
                "XRRigidTransform must be constructed with a position that has a w value of of 1.0, not {}",
                position.w
            )));
        }

        let translate = TypedVector3D::new(position.x as f32, position.y as f32, position.z as f32);
        let rotate = TypedRotation3D::unit_quaternion(
            orientation.x as f32,
            orientation.y as f32,
            orientation.z as f32,
            orientation.w as f32,
        );

        if !rotate.i.is_finite() {
            // if quaternion has zero norm, we'll get an infinite or NaN
            // value for each element. This is preferable to checking for zero.
            return Err(Error::InvalidState);
        }
        let transform = TypedRigidTransform3D::new(rotate, translate);
        Ok(XRRigidTransform::new(&window.global(), transform))
    }
}

impl XRRigidTransformMethods for XRRigidTransform {
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-position
    fn Position(&self) -> DomRoot<DOMPointReadOnly> {
        self.position.or_init(|| {
            let t = &self.transform.translation;
            DOMPointReadOnly::new(&self.global(), t.x.into(), t.y.into(), t.z.into(), 1.0)
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-orientation
    fn Orientation(&self) -> DomRoot<DOMPointReadOnly> {
        self.orientation.or_init(|| {
            let r = &self.transform.rotation;
            DOMPointReadOnly::new(
                &self.global(),
                r.i.into(),
                r.j.into(),
                r.k.into(),
                r.r.into(),
            )
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-inverse
    fn Inverse(&self) -> DomRoot<XRRigidTransform> {
        self.inverse.or_init(|| {
            let transform = XRRigidTransform::new(&self.global(), self.transform.inverse());
            transform.inverse.set(Some(self));
            transform
        })
    }
    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-matrix
    #[allow(unsafe_code)]
    unsafe fn Matrix(&self, _cx: *mut JSContext) -> NonNull<JSObject> {
        if self.matrix.get().is_null() {
            let cx = self.global().get_cx();
            // According to the spec all matrices are column-major,
            // however euclid uses row vectors so we use .to_row_major_array()
            let arr = self.transform.to_transform().to_row_major_array();
            create_typed_array(cx, &arr, &self.matrix);
        }
        NonNull::new(self.matrix.get()).unwrap()
    }
}

impl XRRigidTransform {
    /// https://immersive-web.github.io/webxr/#dom-xrpose-transform
    pub fn transform(&self) -> ApiRigidTransform {
        self.transform
    }
}
<|endoftext|>


<commit_before>use std::fmt::Debug;

use serde::{Deserialize, Serialize};

/// The type of an event.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum EventType {
    /// m.direct
    Direct,

    /// m.room.aliases
    RoomAliases,

    /// m.room.redaction
    RoomRedaction,

    /// Any event that is not part of the specification.
    Custom(String),
}

/// A basic event.
pub trait Event
where
    Self: Debug + Serialize,
{
    /// The type of this event's `content` field.
    type Content: Debug + Serialize;

    /// The event's content.
    fn content(&self) -> &Self::Content;

    /// The type of the event.
    fn event_type(&self) -> EventType;
}

/// An event within the context of a room.
pub trait RoomEvent: Event {
    /// The unique identifier for the event.
    fn event_id(&self) -> &ruma_identifiers::EventId;

    /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this event was
    /// sent.
    fn origin_server_ts(&self) -> js_int::UInt;

    /// The unique identifier for the room associated with this event.
    ///
    /// This can be `None` if the event came from a context where there is
    /// no ambiguity which room it belongs to, like a `/sync` response for example.
    fn room_id(&self) -> Option<&ruma_identifiers::RoomId>;

    /// The unique identifier for the user who sent this event.
    fn sender(&self) -> &ruma_identifiers::UserId;

    /// Additional key-value pairs not signed by the homeserver.
    fn unsigned(&self) -> Option<&serde_json::Value>;
}

/// An event that describes persistent state about a room.
pub trait StateEvent: RoomEvent {
    /// The previous content for this state key, if any.
    fn prev_content(&self) -> Option<&Self::Content>;

    /// A key that determines which piece of room state the event represents.
    fn state_key(&self) -> &str;
}

pub struct InvalidEvent;

impl From<serde_json::Error> for InvalidEvent {
    fn from(_: serde_json::Error) -> Self {
        Self
    }
}

// See note about wrapping macro expansion in a module from `src/lib.rs`
pub mod common_case {
    use ruma_events_macros::ruma_event;

    ruma_event! {
        /// Informs the room about what room aliases it has been given.
        AliasesEvent {
            kind: StateEvent,
            event_type: RoomAliases,
            content: {
                /// A list of room aliases.
                pub aliases: Vec<ruma_identifiers::RoomAliasId>,
            }
        }
    }
}

pub mod custom_event_type {
    use ruma_events_macros::ruma_event;
    use serde_json::Value;

    ruma_event! {
        /// A custom event.
        CustomEvent {
            kind: Event,
            event_type: Custom,
            content_type_alias: {
                /// The payload for `CustomEvent`.
                Value
            },
        }
    }
}

pub mod extra_fields {
    use ruma_events_macros::ruma_event;

    ruma_event! {
        /// A redaction of an event.
        RedactionEvent {
            kind: RoomEvent,
            event_type: RoomRedaction,
            fields: {
                /// The ID of the event that was redacted.
                pub redacts: ruma_identifiers::EventId
            },
            content: {
                /// The reason for the redaction, if any.
                pub reason: Option<String>,
            },
        }
    }
}

pub mod type_alias {
    use ruma_events_macros::ruma_event;

    ruma_event! {
        /// Informs the client about the rooms that are considered direct by a user.
        DirectEvent {
            kind: Event,
            event_type: Direct,
            content_type_alias: {
                /// The payload of a `DirectEvent`.
                ///
                /// A mapping of `UserId`'s to a collection of `RoomId`'s which are considered
                /// *direct* for that particular user.
                std::collections::HashMap<ruma_identifiers::UserId, Vec<ruma_identifiers::RoomId>>
            }
        }
    }
}
<commit_msg>Add serialization and deserialization tests.<commit_after>use std::fmt::{Debug, Display, Formatter, Result as FmtResult};

use serde::{de::{Error as SerdeError, Visitor}, Deserialize, Deserializer, Serialize, Serializer};

/// The type of an event.
#[derive(Clone, Debug, PartialEq)]
pub enum EventType {
    /// m.direct
    Direct,

    /// m.room.aliases
    RoomAliases,

    /// m.room.redaction
    RoomRedaction,

    /// Any event that is not part of the specification.
    Custom(String),
}

impl Display for EventType {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        let event_type_str = match *self {
            EventType::Direct => "m.direct",
            EventType::RoomAliases => "m.room.aliases",
            EventType::RoomRedaction => "m.room.redaction",
            EventType::Custom(ref event_type) => event_type,
        };

        write!(f, "{}", event_type_str)
    }
}

impl<'a> From<&'a str> for EventType {
    fn from(s: &'a str) -> EventType {
        match s {
            "m.direct" => EventType::Direct,
            "m.room.aliases" => EventType::RoomAliases,
            "m.room.redaction" => EventType::RoomRedaction,
            event_type => EventType::Custom(event_type.to_string()),
        }
    }
}

impl Serialize for EventType {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.to_string())
    }
}

impl<'de> Deserialize<'de> for EventType {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct EventTypeVisitor;

        impl<'de> Visitor<'de> for EventTypeVisitor {
            type Value = EventType;

            fn expecting(&self, formatter: &mut Formatter<'_>) -> FmtResult {
                write!(formatter, "a Matrix event type as a string")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: SerdeError,
            {
                Ok(EventType::from(v))
            }
        }

        deserializer.deserialize_str(EventTypeVisitor)
    }
}

/// A basic event.
pub trait Event
where
    Self: Debug + Serialize,
{
    /// The type of this event's `content` field.
    type Content: Debug + Serialize;

    /// The event's content.
    fn content(&self) -> &Self::Content;

    /// The type of the event.
    fn event_type(&self) -> EventType;
}

/// An event within the context of a room.
pub trait RoomEvent: Event {
    /// The unique identifier for the event.
    fn event_id(&self) -> &ruma_identifiers::EventId;

    /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this event was
    /// sent.
    fn origin_server_ts(&self) -> js_int::UInt;

    /// The unique identifier for the room associated with this event.
    ///
    /// This can be `None` if the event came from a context where there is
    /// no ambiguity which room it belongs to, like a `/sync` response for example.
    fn room_id(&self) -> Option<&ruma_identifiers::RoomId>;

    /// The unique identifier for the user who sent this event.
    fn sender(&self) -> &ruma_identifiers::UserId;

    /// Additional key-value pairs not signed by the homeserver.
    fn unsigned(&self) -> Option<&serde_json::Value>;
}

/// An event that describes persistent state about a room.
pub trait StateEvent: RoomEvent {
    /// The previous content for this state key, if any.
    fn prev_content(&self) -> Option<&Self::Content>;

    /// A key that determines which piece of room state the event represents.
    fn state_key(&self) -> &str;
}

#[derive(Debug)]
pub struct InvalidEvent;

impl From<serde_json::Error> for InvalidEvent {
    fn from(_: serde_json::Error) -> Self {
        Self
    }
}

// See note about wrapping macro expansion in a module from `src/lib.rs`
pub mod common_case {
    use std::convert::TryFrom;

    use js_int::UInt;
    use ruma_events_macros::ruma_event;
    use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
    use serde_json::Value;

    ruma_event! {
        /// Informs the room about what room aliases it has been given.
        AliasesEvent {
            kind: StateEvent,
            event_type: RoomAliases,
            content: {
                /// A list of room aliases.
                pub aliases: Vec<ruma_identifiers::RoomAliasId>,
            }
        }
    }

    #[test]
    fn serialization_with_optional_fields_as_none() {
        let event = AliasesEvent {
            content: AliasesEventContent {
                aliases: Vec::with_capacity(0),
            },
            event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
            origin_server_ts: UInt::try_from(1).unwrap(),
            prev_content: None,
            room_id: None,
            sender: UserId::try_from("@carl:example.com").unwrap(),
            state_key: "example.com".to_string(),
            unsigned: None,
        };

        let actual = serde_json::to_string(&event).unwrap();
        let expected = r#"{"content":{"aliases":[]},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"sender":"@carl:example.com","state_key":"example.com","type":"m.room.aliases"}"#;

        assert_eq!(actual, expected);
    }

    #[test]
    fn serialization_with_some_optional_fields_as_some() {
        let event = AliasesEvent {
            content: AliasesEventContent {
                aliases: vec![RoomAliasId::try_from("#room:example.org").unwrap()],
            },
            event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
            origin_server_ts: UInt::try_from(1).unwrap(),
            prev_content: Some(AliasesEventContent {
                aliases: Vec::with_capacity(0),
            }),
            room_id: Some(RoomId::try_from("!n8f893n9:example.com").unwrap()),
            sender: UserId::try_from("@carl:example.com").unwrap(),
            state_key: "example.com".to_string(),
            unsigned: None,
        };

        let actual = serde_json::to_string(&event).unwrap();
        let expected = r##"{"content":{"aliases":["#room:example.org"]},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"prev_content":{"aliases":[]},"room_id":"!n8f893n9:example.com","sender":"@carl:example.com","state_key":"example.com","type":"m.room.aliases"}"##;

        assert_eq!(actual, expected);
    }

    #[test]
    fn serialization_with_all_optional_fields_as_some() {
        let event = AliasesEvent {
            content: AliasesEventContent {
                aliases: vec![RoomAliasId::try_from("#room:example.org").unwrap()],
            },
            event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
            origin_server_ts: UInt::try_from(1).unwrap(),
            prev_content: Some(AliasesEventContent {
                aliases: Vec::with_capacity(0),
            }),
            room_id: Some(RoomId::try_from("!n8f893n9:example.com").unwrap()),
            sender: UserId::try_from("@carl:example.com").unwrap(),
            state_key: "example.com".to_string(),
            unsigned: Some(serde_json::from_str::<Value>(r#"{"foo":"bar"}"#).unwrap()),
        };

        let actual = serde_json::to_string(&event).unwrap();
        let expected = r##"{"content":{"aliases":["#room:example.org"]},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"prev_content":{"aliases":[]},"room_id":"!n8f893n9:example.com","sender":"@carl:example.com","state_key":"example.com","unsigned":{"foo":"bar"},"type":"m.room.aliases"}"##;

        assert_eq!(actual, expected);
    }

    #[test]
    fn deserialization() {
        let json = r##"{"content":{"aliases":["#room:example.org"]},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"prev_content":{"aliases":[]},"room_id":"!n8f893n9:example.com","sender":"@carl:example.com","state_key":"example.com","unsigned":{"foo":"bar"},"type":"m.room.aliases"}"##;

        let actual: AliasesEvent = json.parse().unwrap();

        let expected = AliasesEvent {
            content: AliasesEventContent {
                aliases: vec![RoomAliasId::try_from("#room:example.org").unwrap()],
            },
            event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
            origin_server_ts: UInt::try_from(1).unwrap(),
            prev_content: Some(AliasesEventContent {
                aliases: Vec::with_capacity(0),
            }),
            room_id: Some(RoomId::try_from("!n8f893n9:example.com").unwrap()),
            sender: UserId::try_from("@carl:example.com").unwrap(),
            state_key: "example.com".to_string(),
            unsigned: Some(serde_json::from_str::<Value>(r#"{"foo":"bar"}"#).unwrap()),
        };

        assert_eq!(actual, expected);
    }
}

pub mod custom_event_type {
    use ruma_events_macros::ruma_event;
    use serde_json::Value;

    ruma_event! {
        /// A custom event.
        CustomEvent {
            kind: Event,
            event_type: Custom,
            content_type_alias: {
                /// The payload for `CustomEvent`.
                Value
            },
        }
    }
}

pub mod extra_fields {
    use ruma_events_macros::ruma_event;

    ruma_event! {
        /// A redaction of an event.
        RedactionEvent {
            kind: RoomEvent,
            event_type: RoomRedaction,
            fields: {
                /// The ID of the event that was redacted.
                pub redacts: ruma_identifiers::EventId
            },
            content: {
                /// The reason for the redaction, if any.
                pub reason: Option<String>,
            },
        }
    }
}

pub mod type_alias {
    use ruma_events_macros::ruma_event;

    ruma_event! {
        /// Informs the client about the rooms that are considered direct by a user.
        DirectEvent {
            kind: Event,
            event_type: Direct,
            content_type_alias: {
                /// The payload of a `DirectEvent`.
                ///
                /// A mapping of `UserId`'s to a collection of `RoomId`'s which are considered
                /// *direct* for that particular user.
                std::collections::HashMap<ruma_identifiers::UserId, Vec<ruma_identifiers::RoomId>>
            }
        }
    }
}
<|endoftext|>


<commit_before>#[link(name = "compress")];
#[deny(warnings)];
#[deny(missing_doc)];

extern mod extra;

//mod flate;
pub mod lz4;
<commit_msg>Appease the missing documentation lint<commit_after>#[link(name = "compress")];
#[deny(warnings)];
#[deny(missing_doc)];

//! dox (placeholder)

extern mod extra;

//mod flate;
pub mod lz4;
<|endoftext|>


<commit_before>/*!

LZ4 Decompression

This module contains an implementation in Rust of decompression of LZ4-encoded
streams. This is exposed as a standard `Reader` interface wrapping an
underlying `Reader` interface. In this manner, the decompressor can be viewed
as a stream. It maintains its own internal buffer, but the size of this is
constant as the stream is read.

# Example

```rust
let stream = Path::new("path/to/file.lz4").open_reader(io::Open);
let decompressed = l4z::Decoder::new(stream).read_to_end();
```

# Credit

This implementation is largely based on Branimir Karadžić's implementation which
can be found at https://github.com/bkaradzic/go-lz4.

*/

use std::rt::io;
use std::rt::io::extensions::{ReaderUtil, ReaderByteConversions};
use std::vec;
use std::num;

#[deriving(Eq)]
enum State {
    StreamStart,
    BlockStart,
    BlockEnd,

    // raw blocks of uncompressed data
    RawBlock(u32),

    // blocks of compressed data (no headers or checksums)
    DataStart,
    NeedsConsumption(u8, uint),
    DecompressStart(u8),
    Decompress(uint),

    EndOfStream,
    Cleanup,
}

enum ConsumptionResult {
    KeepGoing(uint),
    Consumed(uint),
    SomethingBad,
}

static BUF_SIZE: uint = 128 << 10;
static FLUSH_SIZE: uint = 1 << 16;
static MAGIC: u32 = 0x184d2204;

/// A decoder of an underlying lz4 stream. This is the decompressor used to
/// inflate an underlying stream.
pub struct Decoder<R> {
    /// This is the stream which is owned by this decoder. It may be moved out
    /// of, but the operation would also invalidate the `Decoder` instance.
    r: R,

    priv state: State,
    priv end: uint,
    priv start: uint,
    priv buf: ~[u8],
    priv remaining_bytes: uint,

    // metadata from the stream descriptor
    priv blk_checksum: bool,
    priv stream_checksum: bool,
}

impl<R: io::Reader + io::Seek> Decoder<R> {
    /// Creates a new decoder which will decompress the LZ4-encoded stream
    /// which will be read from `r`. This decoder will consume ownership of the
    /// reader, but it is accessible via the `r` field on the object returned.
    pub fn new(r: R) -> Decoder<R> {
        Decoder {
            r: r,
            state: StreamStart,
            start: 0,
            end: 0,
            buf: vec::from_elem(BUF_SIZE, 0u8),
            blk_checksum: false,
            stream_checksum: false,
            remaining_bytes: 0,
        }
    }

    fn inner_byte(&mut self) -> Option<u8> {
        match self.r.read_byte() {
            Some(b) => {
                self.remaining_bytes -= 1;
                Some(b)
            }
            None => None,
        }
    }

    fn inner_u16(&mut self) -> u16 {
        self.remaining_bytes -= 2;
        self.r.read_le_u16_()
    }

    fn inner_read(&mut self, dst: &mut [u8]) -> Option<uint> {
        match self.r.read(dst) {
            Some(n) => {
                self.remaining_bytes -= n;
                Some(n)
            }
            None => None,
        }
    }

    fn read_len(&mut self, code: u8) -> Option<uint> {
        let mut ret = code as uint;
        assert!(code < 16);
        if code == 0xf {
            loop {
                match self.inner_byte() {
                    None => return None,
                    Some(255) => ret += 255,
                    Some(n) => {
                        ret += n as uint;
                        break
                    }
                }
            }
        }
        return Some(ret);
    }

    fn consume(&mut self, len: uint, dst: &mut [u8]) -> ConsumptionResult {
        let in_output = self.flush(len, dst);
        if self.end + len > self.buf.len() {
            return KeepGoing(in_output);
        }

        let buf = unsafe {
            use std::cast;
            cast::transmute_copy(&self.buf.mut_slice(self.end, self.end + len))
        };
        match self.inner_read(buf) {
            Some(n) => {
                self.end += n;
                Consumed(in_output)
            }
            None => SomethingBad
        }
    }

    fn flush(&mut self, len: uint, dst: &mut [u8]) -> uint {
        if self.end + len > self.buf.len() {
            assert!(self.start > FLUSH_SIZE);
            let s = self.start - FLUSH_SIZE;

            // Copy bytes out into the destination
            let fill = num::min(dst.len(), s);
            info!("flushing out {}", fill);
            vec::bytes::copy_memory(dst, self.buf, fill);

            // Slide all the bytes back down in the internal buffer
            for i in range(0, fill) {
                self.buf[i] = self.buf[fill + i];
            }

            self.end -= fill;
            self.start -= fill;
            return fill;
        }
        return 0;
    }

    fn cp(&mut self, len: uint, decr: uint, dst: &mut [u8]) -> ConsumptionResult {
        let in_output = self.flush(len, dst);
        if self.end + len > self.buf.len() {
            return KeepGoing(in_output);
        }

        for i in range(0, len) {
            self.buf[self.end + i] = self.buf[self.start + i];
        }

        self.end += len;
        self.start += len - decr;
        return Consumed(in_output);
    }

    fn exec(&mut self, mut state: State, buf: &mut [u8]) -> Option<uint> {
        let mut offset = 0;
        loop {
            debug!("state: {:?} {:x} {:x} at {}", state, self.start, self.end,
                   self.r.tell());
            match state {
                StreamStart => {
                    match self.header() {
                        Some(()) => {}
                        None => return None,
                    }
                    state = BlockStart;
                }

                BlockStart => {
                    state = match self.r.read_le_u32_() {
                        0 => EndOfStream,
                        n if n & 0x80000000 != 0 => RawBlock(n & 0x7fffffff),
                        n => {
                            self.remaining_bytes = n as uint;
                            info!("bytes left {}", self.remaining_bytes);
                            DataStart
                        }
                    }
                }

                DataStart => {
                    //info!("remaining: {}", self.remaining_bytes as int);
                    assert!(self.remaining_bytes as int > 0);
                    let code = match self.inner_byte() {
                        Some(i) => i,
                        None => return None,
                    };
                    debug!("at: {:x} {} {:x}", self.r.tell(),
                           self.remaining_bytes as int, code);

                    // XXX: I/O errors
                    let len = match self.read_len(code >> 4) {
                        Some(l) => l, None => return None,
                    };
                    state = NeedsConsumption(code, len);
                }

                NeedsConsumption(code, len) => {
                    match self.consume(len, buf.mut_slice_from(offset)) {
                        // XXX: I/O error
                        SomethingBad => return None,
                        KeepGoing(amt_written) => {
                            self.state = state;
                            return Some(amt_written + offset);
                        }
                        Consumed(amt_written) => {
                            offset += amt_written;
                            if self.remaining_bytes == 0 {
                                state = BlockStart;
                            } else {
                                state = DecompressStart(code);
                            }
                        }
                    }
                }

                DecompressStart(code) => {
                    // XXX: I/O errors
                    let back = self.inner_u16();
                    debug!("got back: {}", back);
                    self.start = self.end - back as uint;

                    let len = match self.read_len(code & 0xf) {
                        Some(l) => l, None => return None,
                    };

                    state = Decompress(len);
                }

                Decompress(len) => {
                    let literal = self.end - self.start;
                    if literal < 4 {
                        static DECR: [uint, ..4] = [0, 3, 2, 3];
                        match self.cp(4, DECR[literal],
                                      buf.mut_slice_from(offset)) {
                            SomethingBad => unreachable!(),
                            KeepGoing(amt_written) => {
                                self.state = state;
                                return Some(amt_written + offset);
                            }
                            Consumed(amt_written) => offset += amt_written,
                        }
                    } else {
                        len += 4;
                    }
                    match self.cp(len, 0, buf.mut_slice_from(offset)) {
                        SomethingBad => unreachable!(),
                        KeepGoing(amt_written) => {
                            self.state = state;
                            return Some(amt_written + offset);
                        }
                        Consumed(amt_written) => {
                            offset += amt_written;
                            state = DataStart;
                        }
                    }
                }

                RawBlock(remaining) => {
                    let dst = buf.mut_slice_from(offset);
                    if dst.len() == 0 {
                        self.state = state;
                        return Some(offset);
                    }
                    if remaining == 0 {
                        state = BlockEnd;
                        continue
                    }
                    let amt = num::min(remaining as uint, dst.len());
                    match self.r.read(dst.mut_slice(offset, offset + amt)) {
                        Some(n) => {
                            offset += n;
                            state = RawBlock(remaining - n as u32);
                        }
                        None => return None
                    }
                }

                BlockEnd => {
                    // XXX: implement checksums
                    if self.blk_checksum {
                        let cksum = self.r.read_le_u32_();
                        debug!("ignoring block cksum: {:?}", cksum);
                    }
                    state = BlockStart;
                }

                EndOfStream => {
                    // XXX: implement checksums
                    if self.stream_checksum {
                        let cksum = self.r.read_le_u32_();
                        debug!("ignoring stream checksum: {:?}", cksum);
                    }
                    state = Cleanup;
                    self.start = 0;
                }

                Cleanup => {
                    let dst = buf.mut_slice_from(offset);
                    let src = self.buf.slice(self.start, self.end);
                    let amt = num::min(dst.len(), src.len());
                    if amt == 0 { return None }
                    vec::bytes::copy_memory(dst, src, amt);
                    self.start += amt;
                    self.state = Cleanup;
                    return Some(amt + offset);
                }
            }
        }
    }

    fn header(&mut self) -> Option<()> {
        // Make sure the magic number is what's expected.
        if self.r.read_le_u32_() != MAGIC { return None }

        let flg = match self.r.read_byte() { Some(i) => i, None => return None };
        let bd = match self.r.read_byte() { Some(i) => i, None => return None };

        // bits 7/6, the version number. Right now this must be 1
        if (flg >> 6) != 0b01 { return None }
        // bit 5 is the "block independence", don't care about this yet
        // bit 4 is whether blocks have checksums or not
        self.blk_checksum = (flg & 0x10) != 0;
        // bit 3 is whether there is a following stream size
        let stream_size = (flg & 0x08) != 0;
        // bit 2 is whether there is a stream checksum
        self.stream_checksum = (flg & 0x04) != 0;
        // bit 1 is reserved
        // bit 0 is whether there is a preset dictionary
        let preset_dictionary = (flg & 0x01) != 0;

        static MAX_SIZES: [uint, ..8] =
            [0, 0, 0, 0, // all N/A
             64 << 10,   // 64KB
             256 << 10,  // 256 KB
             1 << 20,    // 1MB
             4 << 20];   // 4MB

        // bit 7 is reserved
        // bits 6-4 are the maximum block size
        let max_block_size = MAX_SIZES[(bd >> 4) & 0x7];
        // bits 3-0 are reserved

        // read off other portions of the stream
        let size = if stream_size {Some(self.r.read_le_u64_())} else {None};
        assert!(!preset_dictionary, "preset dictionaries not supported yet");

        debug!("blk: {}", self.blk_checksum);
        debug!("stream: {}", self.stream_checksum);
        debug!("max size: {}", max_block_size);
        debug!("stream size: {:?}", size);

        // XXX: implement checksums
        let cksum = self.r.read_byte();
        debug!("ignoring header checksum: {:?}", cksum);
        return Some(());
    }
}

impl<R: io::Reader + io::Seek> io::Reader for Decoder<R> {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        info!("reading {}", buf.len());
        let out = self.exec(self.state, buf);
        info!("got {:?}", out);
        out
    }

    fn eof(&mut self) -> bool { fail!() }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::rt::io::extensions::ReaderUtil;
    use std::rt::io::mem::BufReader;
    use std::rt::io::Reader;
    use std::rand;

    fn test_decode(input: &[u8], output: &[u8]) {
        let mut d = Decoder::new(BufReader::new(input));

        let got = d.read_to_end();
        assert!(got.as_slice() == output);
    }

    #[test]
    fn decode() {
        let reference = include_bin!("data/test.txt");
        test_decode(include_bin!("data/test.lz4.1"), reference);
        test_decode(include_bin!("data/test.lz4.2"), reference);
        test_decode(include_bin!("data/test.lz4.3"), reference);
        test_decode(include_bin!("data/test.lz4.4"), reference);
        test_decode(include_bin!("data/test.lz4.5"), reference);
        test_decode(include_bin!("data/test.lz4.6"), reference);
        test_decode(include_bin!("data/test.lz4.7"), reference);
        test_decode(include_bin!("data/test.lz4.8"), reference);
        test_decode(include_bin!("data/test.lz4.9"), reference);
    }

    #[test]
    fn one_byte_at_a_time() {
        let input = include_bin!("data/test.lz4.1");
        let mut d = Decoder::new(BufReader::new(input));
        let mut out = ~[];
        loop {
            match d.read_byte() {
                Some(b) => out.push(b),
                None => break
            }
        }
        assert!(out.as_slice() == include_bin!("data/test.txt"));
    }

    #[test]
    fn random_byte_lengths() {
        let input = include_bin!("data/test.lz4.1");
        let mut d = Decoder::new(BufReader::new(input));
        let mut out = ~[];
        let mut buf = [0u8, ..40];
        loop {
            match d.read(buf.mut_slice_to(1 + rand::random::<uint>() % 40)) {
                Some(n) => {
                    out.push_all(buf.slice_to(n));
                }
                None => break
            }
        }
        assert!(out.as_slice() == include_bin!("data/test.txt"));
    }
}
<commit_msg>Don't fail on eof, but don't return anything useful either<commit_after>/*!

LZ4 Decompression

This module contains an implementation in Rust of decompression of LZ4-encoded
streams. This is exposed as a standard `Reader` interface wrapping an
underlying `Reader` interface. In this manner, the decompressor can be viewed
as a stream. It maintains its own internal buffer, but the size of this is
constant as the stream is read.

# Example

```rust
let stream = Path::new("path/to/file.lz4").open_reader(io::Open);
let decompressed = l4z::Decoder::new(stream).read_to_end();
```

# Credit

This implementation is largely based on Branimir Karadžić's implementation which
can be found at https://github.com/bkaradzic/go-lz4.

*/

use std::rt::io;
use std::rt::io::extensions::{ReaderUtil, ReaderByteConversions};
use std::vec;
use std::num;

#[deriving(Eq)]
enum State {
    StreamStart,
    BlockStart,
    BlockEnd,

    // raw blocks of uncompressed data
    RawBlock(u32),

    // blocks of compressed data (no headers or checksums)
    DataStart,
    NeedsConsumption(u8, uint),
    DecompressStart(u8),
    Decompress(uint),

    EndOfStream,
    Cleanup,
}

enum ConsumptionResult {
    KeepGoing(uint),
    Consumed(uint),
    SomethingBad,
}

static BUF_SIZE: uint = 128 << 10;
static FLUSH_SIZE: uint = 1 << 16;
static MAGIC: u32 = 0x184d2204;

/// A decoder of an underlying lz4 stream. This is the decompressor used to
/// inflate an underlying stream.
pub struct Decoder<R> {
    /// This is the stream which is owned by this decoder. It may be moved out
    /// of, but the operation would also invalidate the `Decoder` instance.
    r: R,

    priv state: State,
    priv end: uint,
    priv start: uint,
    priv buf: ~[u8],
    priv remaining_bytes: uint,

    // metadata from the stream descriptor
    priv blk_checksum: bool,
    priv stream_checksum: bool,
}

impl<R: io::Reader + io::Seek> Decoder<R> {
    /// Creates a new decoder which will decompress the LZ4-encoded stream
    /// which will be read from `r`. This decoder will consume ownership of the
    /// reader, but it is accessible via the `r` field on the object returned.
    pub fn new(r: R) -> Decoder<R> {
        Decoder {
            r: r,
            state: StreamStart,
            start: 0,
            end: 0,
            buf: vec::from_elem(BUF_SIZE, 0u8),
            blk_checksum: false,
            stream_checksum: false,
            remaining_bytes: 0,
        }
    }

    fn inner_byte(&mut self) -> Option<u8> {
        match self.r.read_byte() {
            Some(b) => {
                self.remaining_bytes -= 1;
                Some(b)
            }
            None => None,
        }
    }

    fn inner_u16(&mut self) -> u16 {
        self.remaining_bytes -= 2;
        self.r.read_le_u16_()
    }

    fn inner_read(&mut self, dst: &mut [u8]) -> Option<uint> {
        match self.r.read(dst) {
            Some(n) => {
                self.remaining_bytes -= n;
                Some(n)
            }
            None => None,
        }
    }

    fn read_len(&mut self, code: u8) -> Option<uint> {
        let mut ret = code as uint;
        assert!(code < 16);
        if code == 0xf {
            loop {
                match self.inner_byte() {
                    None => return None,
                    Some(255) => ret += 255,
                    Some(n) => {
                        ret += n as uint;
                        break
                    }
                }
            }
        }
        return Some(ret);
    }

    fn consume(&mut self, len: uint, dst: &mut [u8]) -> ConsumptionResult {
        let in_output = self.flush(len, dst);
        if self.end + len > self.buf.len() {
            return KeepGoing(in_output);
        }

        let buf = unsafe {
            use std::cast;
            cast::transmute_copy(&self.buf.mut_slice(self.end, self.end + len))
        };
        match self.inner_read(buf) {
            Some(n) => {
                self.end += n;
                Consumed(in_output)
            }
            None => SomethingBad
        }
    }

    fn flush(&mut self, len: uint, dst: &mut [u8]) -> uint {
        if self.end + len > self.buf.len() {
            assert!(self.start > FLUSH_SIZE);
            let s = self.start - FLUSH_SIZE;

            // Copy bytes out into the destination
            let fill = num::min(dst.len(), s);
            info!("flushing out {}", fill);
            vec::bytes::copy_memory(dst, self.buf, fill);

            // Slide all the bytes back down in the internal buffer
            for i in range(0, fill) {
                self.buf[i] = self.buf[fill + i];
            }

            self.end -= fill;
            self.start -= fill;
            return fill;
        }
        return 0;
    }

    fn cp(&mut self, len: uint, decr: uint, dst: &mut [u8]) -> ConsumptionResult {
        let in_output = self.flush(len, dst);
        if self.end + len > self.buf.len() {
            return KeepGoing(in_output);
        }

        for i in range(0, len) {
            self.buf[self.end + i] = self.buf[self.start + i];
        }

        self.end += len;
        self.start += len - decr;
        return Consumed(in_output);
    }

    fn exec(&mut self, mut state: State, buf: &mut [u8]) -> Option<uint> {
        let mut offset = 0;
        loop {
            debug!("state: {:?} {:x} {:x} at {}", state, self.start, self.end,
                   self.r.tell());
            match state {
                StreamStart => {
                    match self.header() {
                        Some(()) => {}
                        None => return None,
                    }
                    state = BlockStart;
                }

                BlockStart => {
                    state = match self.r.read_le_u32_() {
                        0 => EndOfStream,
                        n if n & 0x80000000 != 0 => RawBlock(n & 0x7fffffff),
                        n => {
                            self.remaining_bytes = n as uint;
                            info!("bytes left {}", self.remaining_bytes);
                            DataStart
                        }
                    }
                }

                DataStart => {
                    //info!("remaining: {}", self.remaining_bytes as int);
                    assert!(self.remaining_bytes as int > 0);
                    let code = match self.inner_byte() {
                        Some(i) => i,
                        None => return None,
                    };
                    debug!("at: {:x} {} {:x}", self.r.tell(),
                           self.remaining_bytes as int, code);

                    // XXX: I/O errors
                    let len = match self.read_len(code >> 4) {
                        Some(l) => l, None => return None,
                    };
                    state = NeedsConsumption(code, len);
                }

                NeedsConsumption(code, len) => {
                    match self.consume(len, buf.mut_slice_from(offset)) {
                        // XXX: I/O error
                        SomethingBad => return None,
                        KeepGoing(amt_written) => {
                            self.state = state;
                            return Some(amt_written + offset);
                        }
                        Consumed(amt_written) => {
                            offset += amt_written;
                            if self.remaining_bytes == 0 {
                                state = BlockStart;
                            } else {
                                state = DecompressStart(code);
                            }
                        }
                    }
                }

                DecompressStart(code) => {
                    // XXX: I/O errors
                    let back = self.inner_u16();
                    debug!("got back: {}", back);
                    self.start = self.end - back as uint;

                    let len = match self.read_len(code & 0xf) {
                        Some(l) => l, None => return None,
                    };

                    state = Decompress(len);
                }

                Decompress(len) => {
                    let literal = self.end - self.start;
                    if literal < 4 {
                        static DECR: [uint, ..4] = [0, 3, 2, 3];
                        match self.cp(4, DECR[literal],
                                      buf.mut_slice_from(offset)) {
                            SomethingBad => unreachable!(),
                            KeepGoing(amt_written) => {
                                self.state = state;
                                return Some(amt_written + offset);
                            }
                            Consumed(amt_written) => offset += amt_written,
                        }
                    } else {
                        len += 4;
                    }
                    match self.cp(len, 0, buf.mut_slice_from(offset)) {
                        SomethingBad => unreachable!(),
                        KeepGoing(amt_written) => {
                            self.state = state;
                            return Some(amt_written + offset);
                        }
                        Consumed(amt_written) => {
                            offset += amt_written;
                            state = DataStart;
                        }
                    }
                }

                RawBlock(remaining) => {
                    let dst = buf.mut_slice_from(offset);
                    if dst.len() == 0 {
                        self.state = state;
                        return Some(offset);
                    }
                    if remaining == 0 {
                        state = BlockEnd;
                        continue
                    }
                    let amt = num::min(remaining as uint, dst.len());
                    match self.r.read(dst.mut_slice(offset, offset + amt)) {
                        Some(n) => {
                            offset += n;
                            state = RawBlock(remaining - n as u32);
                        }
                        None => return None
                    }
                }

                BlockEnd => {
                    // XXX: implement checksums
                    if self.blk_checksum {
                        let cksum = self.r.read_le_u32_();
                        debug!("ignoring block cksum: {:?}", cksum);
                    }
                    state = BlockStart;
                }

                EndOfStream => {
                    // XXX: implement checksums
                    if self.stream_checksum {
                        let cksum = self.r.read_le_u32_();
                        debug!("ignoring stream checksum: {:?}", cksum);
                    }
                    state = Cleanup;
                    self.start = 0;
                }

                Cleanup => {
                    let dst = buf.mut_slice_from(offset);
                    let src = self.buf.slice(self.start, self.end);
                    let amt = num::min(dst.len(), src.len());
                    if amt == 0 { return None }
                    vec::bytes::copy_memory(dst, src, amt);
                    self.start += amt;
                    self.state = Cleanup;
                    return Some(amt + offset);
                }
            }
        }
    }

    fn header(&mut self) -> Option<()> {
        // Make sure the magic number is what's expected.
        if self.r.read_le_u32_() != MAGIC { return None }

        let flg = match self.r.read_byte() { Some(i) => i, None => return None };
        let bd = match self.r.read_byte() { Some(i) => i, None => return None };

        // bits 7/6, the version number. Right now this must be 1
        if (flg >> 6) != 0b01 { return None }
        // bit 5 is the "block independence", don't care about this yet
        // bit 4 is whether blocks have checksums or not
        self.blk_checksum = (flg & 0x10) != 0;
        // bit 3 is whether there is a following stream size
        let stream_size = (flg & 0x08) != 0;
        // bit 2 is whether there is a stream checksum
        self.stream_checksum = (flg & 0x04) != 0;
        // bit 1 is reserved
        // bit 0 is whether there is a preset dictionary
        let preset_dictionary = (flg & 0x01) != 0;

        static MAX_SIZES: [uint, ..8] =
            [0, 0, 0, 0, // all N/A
             64 << 10,   // 64KB
             256 << 10,  // 256 KB
             1 << 20,    // 1MB
             4 << 20];   // 4MB

        // bit 7 is reserved
        // bits 6-4 are the maximum block size
        let max_block_size = MAX_SIZES[(bd >> 4) & 0x7];
        // bits 3-0 are reserved

        // read off other portions of the stream
        let size = if stream_size {Some(self.r.read_le_u64_())} else {None};
        assert!(!preset_dictionary, "preset dictionaries not supported yet");

        debug!("blk: {}", self.blk_checksum);
        debug!("stream: {}", self.stream_checksum);
        debug!("max size: {}", max_block_size);
        debug!("stream size: {:?}", size);

        // XXX: implement checksums
        let cksum = self.r.read_byte();
        debug!("ignoring header checksum: {:?}", cksum);
        return Some(());
    }
}

impl<R: io::Reader + io::Seek> io::Reader for Decoder<R> {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        info!("reading {}", buf.len());
        let out = self.exec(self.state, buf);
        info!("got {:?}", out);
        out
    }

    fn eof(&mut self) -> bool { false }
}

#[cfg(test)]
mod test {
    use super::Decoder;
    use std::rt::io::extensions::ReaderUtil;
    use std::rt::io::mem::BufReader;
    use std::rt::io::Reader;
    use std::rand;

    fn test_decode(input: &[u8], output: &[u8]) {
        let mut d = Decoder::new(BufReader::new(input));

        let got = d.read_to_end();
        assert!(got.as_slice() == output);
    }

    #[test]
    fn decode() {
        let reference = include_bin!("data/test.txt");
        test_decode(include_bin!("data/test.lz4.1"), reference);
        test_decode(include_bin!("data/test.lz4.2"), reference);
        test_decode(include_bin!("data/test.lz4.3"), reference);
        test_decode(include_bin!("data/test.lz4.4"), reference);
        test_decode(include_bin!("data/test.lz4.5"), reference);
        test_decode(include_bin!("data/test.lz4.6"), reference);
        test_decode(include_bin!("data/test.lz4.7"), reference);
        test_decode(include_bin!("data/test.lz4.8"), reference);
        test_decode(include_bin!("data/test.lz4.9"), reference);
    }

    #[test]
    fn one_byte_at_a_time() {
        let input = include_bin!("data/test.lz4.1");
        let mut d = Decoder::new(BufReader::new(input));
        let mut out = ~[];
        loop {
            match d.read_byte() {
                Some(b) => out.push(b),
                None => break
            }
        }
        assert!(out.as_slice() == include_bin!("data/test.txt"));
    }

    #[test]
    fn random_byte_lengths() {
        let input = include_bin!("data/test.lz4.1");
        let mut d = Decoder::new(BufReader::new(input));
        let mut out = ~[];
        let mut buf = [0u8, ..40];
        loop {
            match d.read(buf.mut_slice_to(1 + rand::random::<uint>() % 40)) {
                Some(n) => {
                    out.push_all(buf.slice_to(n));
                }
                None => break
            }
        }
        assert!(out.as_slice() == include_bin!("data/test.txt"));
    }
}
<|endoftext|>