<commit_before>pub mod messages;

use address::Address;
use mio;
use network;
use self::messages::{Message, TextMessage, MessageAcknowledgement, Envelope};
use std::collections::{HashMap, HashSet};
use std::sync::mpsc;
use std::thread;

/// A command for the Client to execute immediately.
#[derive(Debug)]
pub enum Task {

    /// Handle events emitted by the `Network`, e.g. handling packets that we receive.
    HandleNetworkEvent(network::Event),

    /// Schedules a message to be delivered.
    ScheduleMessageDelivery(Address, TextMessage),

    /// Shuts down the `Client`. When it has completed the shutdown procedure, it will emit an
    /// `Event::Shutdown`.
    Shutdown
}

/// A task to be performed some time in the future
#[derive(Debug)]
pub enum ScheduledTask {

    /// Deliver a message to an address. This can be a message we are sending to someone else, or
    /// just a message we're relaying.
    DeliverMessage(Address, TextMessage),
}

/// Events emitted to any listeners registered with `register_event_listener`. They represent
/// various client-level events.
#[derive(Clone, Debug)]
pub enum Event {
    /// We've received a message that was addressed to us
    ReceivedTextMessage(TextMessage),

    /// We've sent or relayed a message
    SentTextMessage(TextMessage),

    /// We've received an acknowledgement for a message we sent
    ReceivedMessageAcknowledgement(MessageAcknowledgement),

    /// The client has shut down
    ///
    /// TODO: It might be useful for `Shutdown` to contain the serialized state of the entire
    /// `Client`, `Network`, `RoutingTable`, `Bucket`s and `Nodes` so that a user may store it and
    /// later re-initialize a `Client` with this state in order to prevent re-bootstrapping from
    /// scratch.
    Shutdown,

    /// `Network` has started running. This does not necessary guarantee that we are "connected" to
    /// the network, because we haven't necessarily sent/received any messages. It just indicates
    /// that the `Network` is open for business.
    Started
}

/// A sender for issuing commands to the `Client`. Once a client is `run`, it is consumed and
/// methods cannot be called on it. A `TaskSender` as the asynchronous interface for controlling a
/// running `Client`.
pub type TaskSender = mio::Sender<Task>;

/// A receiver for receiving events from a running `Client`. Similar in purpose to `TaskSender`. A
/// running `Client` is consumed and cannot be queried via method calls. You must instead subscribe
/// to events it emits
pub type Events = mpsc::Receiver<Event>;

/// A `Client` handles receiving, processing, relaying, etc. of messages in the communication
/// network. It internalizes all the complex logic that is specific to messaging, and exposes a few
/// commands view a `TaskSender` and events via `Events`.
pub struct Client {
    address: Address,
    network_commands: Option<network::TaskSender>,
    received: HashSet<Address>,
    pending_deliveries: HashMap<Address, mio::Timeout>,
    delivered: HashMap<Address, usize>,
    acknowledgements: HashMap<Address, MessageAcknowledgement>,
    event_listeners: Vec<mpsc::Sender<Event>>
}

impl Client {
    /// Creates a new `Client`. `address` is the address of this client, i.e. where other clients
    /// should send messages intended for this client.
    pub fn new(address: Address) -> Client {
        Client {
            address: address,
            network_commands: None,
            received: HashSet::new(),
            pending_deliveries: HashMap::new(),
            delivered: HashMap::new(),
            acknowledgements: HashMap::new(),
            event_listeners: Vec::new()
        }
    }

    /// Starts a `Client` in its own thread and returns its task sender. This method consumes the
    /// `Client`, so all event listeners must be registered first.
    pub fn run(mut self, mut network: network::Network) -> TaskSender {
        let mut event_loop = mio::EventLoop::new().unwrap();
        let (event_sender, event_receiver) = mpsc::channel();
        network.register_event_listener(event_sender);
        let notify_channel = event_loop.channel();

        thread::spawn(move|| {
            for event in event_receiver.iter() {
                notify_channel
                    .send(Task::HandleNetworkEvent(event))
                    .unwrap_or_else(|err| info!("Couldn't handle network event: {:?}", err));
            }
        });

        self.network_commands = Some(network.run());

        let notify_channel = event_loop.channel();
        info!("Running client at {}", self.address);
        thread::spawn(move || event_loop.run(&mut self).unwrap());
        notify_channel
    }

    /// Registers an event listener that should be sent every `Event` the client emits.
    pub fn register_event_listener(&mut self, event_listener: mpsc::Sender<Event>) {
        self.event_listeners.push(event_listener);
    }

    fn handle_networking_event(&mut self, event: network::Event, event_loop: &mut mio::EventLoop<Client>) {
        match event {
            network::Event::ReceivedPacket(sender, data) => {
                let envelope = messages::decode(data);
                let Envelope { recipient, .. } = envelope;

                match envelope.message {
                    Message::TextMessage(text_message) => {
                        if recipient == self.address {
                            if self.received.insert(text_message.id) {
                                let ack = MessageAcknowledgement::new(text_message.id);
                                let sender = text_message.sender;
                                let event = Event::ReceivedTextMessage(text_message);
                                self.broadcast_event(event);
                                self.deliver_acknowledgement(sender, ack, event_loop);
                            }
                        } else {
                            if let Some(ack) = self.acknowledgements.remove(&text_message.id) {
                                self.deliver_acknowledgement(sender, ack.clone(), event_loop);
                                self.acknowledgements.insert(text_message.id, ack);
                            } else {
                                self.schedule_message_delivery(recipient, text_message, event_loop);
                            }
                        }
                    }
                    Message::MessageAcknowledgement(ack) => {
                        debug!("ack {}", ack.message_id);
                        self.pending_deliveries.remove(&ack.message_id).map(|p| event_loop.clear_timeout(p));

                        if let None = self.acknowledgements.insert(ack.message_id, ack.clone()) {
                            if recipient == self.address {
                                self.broadcast_event(Event::ReceivedMessageAcknowledgement(ack));
                            } else {
                                self.deliver_acknowledgement(recipient, ack, event_loop);
                            }
                        }
                    }
                }
            }

            network::Event::Shutdown => {
                event_loop.shutdown();
                self.broadcast_event(Event::Shutdown);
            }

            network::Event::Started => {
                self.broadcast_event(Event::Started);
            }
        }
    }

    fn schedule_message_delivery(&mut self, recipient: Address, text_message: TextMessage, event_loop: &mut mio::EventLoop<Client>) {
        let message_id = text_message.id;
        if !self.pending_deliveries.contains_key(&message_id) {
            let delivered = self.delivered.entry(message_id).or_insert(0);
            let delay = (2u64.pow(*delivered as u32) - 1) * 1000;
            debug!("Deliver {} with delay {:?}", message_id, delay);
            let timeout = event_loop.timeout_ms(ScheduledTask::DeliverMessage(recipient, text_message.clone()), delay).unwrap();
            self.pending_deliveries.insert(message_id, timeout);
            *delivered += 1;
        }
    }

    fn deliver_acknowledgement(&mut self, recipient: Address, acknowledgement: MessageAcknowledgement, _event_loop: &mut mio::EventLoop<Client>) {
        if let Some(ref commands) = self.network_commands {
            let envelope = acknowledgement.envelope(recipient);
            commands.send(network::OneshotTask::SendPacket(recipient, envelope.encode())).unwrap();
        }
    }

    fn deliver_message(&mut self, recipient: Address, text_message: TextMessage, event_loop: &mut mio::EventLoop<Client>) {
        let delivered = self.network_commands.as_ref().map(|commands| {
            let envelope = text_message.clone().envelope(recipient);
            commands.send(network::OneshotTask::SendPacket(recipient, envelope.encode()))
        }).unwrap();

        if let Ok(_) = delivered {
            self.pending_deliveries.remove(&text_message.id);
            self.schedule_message_delivery(recipient, text_message.clone(), event_loop);
            self.broadcast_event(Event::SentTextMessage(text_message));
        }
    }

    fn broadcast_event(&self, event: Event) {
        for listener in self.event_listeners.iter() {
            listener.send(event.clone()).expect("Could not broadcast event");
        }
    }

    fn shutdown(&self, _event_loop: &mut mio::EventLoop<Client>) {
        if let Some(ref commands) = self.network_commands {
            commands.send(network::OneshotTask::Shutdown).unwrap();
        }
    }
}

impl mio::Handler for Client {
    type Timeout = ScheduledTask;
    type Message = Task;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Client>, task: Task) {
        match task {
            Task::HandleNetworkEvent(event) => self.handle_networking_event(event, event_loop),
            Task::ScheduleMessageDelivery(recipient, message) => self.schedule_message_delivery(recipient, message, event_loop),
            Task::Shutdown => self.shutdown(event_loop)
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Client>, task: ScheduledTask) {
        match task {
            ScheduledTask::DeliverMessage(recipient, message) => self.deliver_message(recipient, message, event_loop)
        }
    }
}
<commit_msg>Add some logging to Client<commit_after>pub mod messages;

use address::Address;
use mio;
use network;
use self::messages::{Message, TextMessage, MessageAcknowledgement, Envelope};
use std::collections::{HashMap, HashSet};
use std::sync::mpsc;
use std::thread;

/// A command for the Client to execute immediately.
#[derive(Debug)]
pub enum Task {

    /// Handle events emitted by the `Network`, e.g. handling packets that we receive.
    HandleNetworkEvent(network::Event),

    /// Schedules a message to be delivered.
    ScheduleMessageDelivery(Address, TextMessage),

    /// Shuts down the `Client`. When it has completed the shutdown procedure, it will emit an
    /// `Event::Shutdown`.
    Shutdown
}

/// A task to be performed some time in the future
#[derive(Debug)]
pub enum ScheduledTask {

    /// Deliver a message to an address. This can be a message we are sending to someone else, or
    /// just a message we're relaying.
    DeliverMessage(Address, TextMessage),
}

/// Events emitted to any listeners registered with `register_event_listener`. They represent
/// various client-level events.
#[derive(Clone, Debug)]
pub enum Event {
    /// We've received a message that was addressed to us
    ReceivedTextMessage(TextMessage),

    /// We've sent or relayed a message
    SentTextMessage(TextMessage),

    /// We've received an acknowledgement for a message we sent
    ReceivedMessageAcknowledgement(MessageAcknowledgement),

    /// The client has shut down
    ///
    /// TODO: It might be useful for `Shutdown` to contain the serialized state of the entire
    /// `Client`, `Network`, `RoutingTable`, `Bucket`s and `Nodes` so that a user may store it and
    /// later re-initialize a `Client` with this state in order to prevent re-bootstrapping from
    /// scratch.
    Shutdown,

    /// `Network` has started running. This does not necessary guarantee that we are "connected" to
    /// the network, because we haven't necessarily sent/received any messages. It just indicates
    /// that the `Network` is open for business.
    Started
}

/// A sender for issuing commands to the `Client`. Once a client is `run`, it is consumed and
/// methods cannot be called on it. A `TaskSender` as the asynchronous interface for controlling a
/// running `Client`.
pub type TaskSender = mio::Sender<Task>;

/// A receiver for receiving events from a running `Client`. Similar in purpose to `TaskSender`. A
/// running `Client` is consumed and cannot be queried via method calls. You must instead subscribe
/// to events it emits
pub type Events = mpsc::Receiver<Event>;

/// A `Client` handles receiving, processing, relaying, etc. of messages in the communication
/// network. It internalizes all the complex logic that is specific to messaging, and exposes a few
/// commands view a `TaskSender` and events via `Events`.
pub struct Client {
    address: Address,
    network_commands: Option<network::TaskSender>,
    received: HashSet<Address>,
    pending_deliveries: HashMap<Address, mio::Timeout>,
    delivered: HashMap<Address, usize>,
    acknowledgements: HashMap<Address, MessageAcknowledgement>,
    event_listeners: Vec<mpsc::Sender<Event>>
}

impl Client {
    /// Creates a new `Client`. `address` is the address of this client, i.e. where other clients
    /// should send messages intended for this client.
    pub fn new(address: Address) -> Client {
        Client {
            address: address,
            network_commands: None,
            received: HashSet::new(),
            pending_deliveries: HashMap::new(),
            delivered: HashMap::new(),
            acknowledgements: HashMap::new(),
            event_listeners: Vec::new()
        }
    }

    /// Starts a `Client` in its own thread and returns its task sender. This method consumes the
    /// `Client`, so all event listeners must be registered first.
    pub fn run(mut self, mut network: network::Network) -> TaskSender {
        let mut event_loop = mio::EventLoop::new().unwrap();
        let (event_sender, event_receiver) = mpsc::channel();
        network.register_event_listener(event_sender);
        let notify_channel = event_loop.channel();

        thread::spawn(move|| {
            for event in event_receiver.iter() {
                notify_channel
                    .send(Task::HandleNetworkEvent(event))
                    .unwrap_or_else(|err| info!("Couldn't handle network event: {:?}", err));
            }
        });

        self.network_commands = Some(network.run());

        let notify_channel = event_loop.channel();
        info!("Running client at {}", self.address);
        thread::spawn(move || event_loop.run(&mut self).unwrap());
        notify_channel
    }

    /// Registers an event listener that should be sent every `Event` the client emits.
    pub fn register_event_listener(&mut self, event_listener: mpsc::Sender<Event>) {
        self.event_listeners.push(event_listener);
    }

    fn handle_networking_event(&mut self, event: network::Event, event_loop: &mut mio::EventLoop<Client>) {
        match event {
            network::Event::ReceivedPacket(sender, data) => {
                let envelope = messages::decode(data);
                let Envelope { recipient, .. } = envelope;

                match envelope.message {
                    Message::TextMessage(text_message) => {
                        if recipient == self.address {
                            if self.received.insert(text_message.id) {
                                debug!("Received message for me: {:?} via sender {}", &text_message, &sender);
                                let ack = MessageAcknowledgement::new(text_message.id);
                                let sender = text_message.sender;
                                let event = Event::ReceivedTextMessage(text_message);
                                self.broadcast_event(event);
                                self.deliver_acknowledgement(sender, ack, event_loop);
                            }
                        } else {
                            if let Some(ack) = self.acknowledgements.remove(&text_message.id) {
                                debug!("Received already acknowledged message {:?} received via sender {}", &text_message, &sender);
                                self.deliver_acknowledgement(sender, ack.clone(), event_loop);
                                self.acknowledgements.insert(text_message.id, ack);
                            } else {
                                debug!("Received message for {}: {:?} via sender {}", &recipient, &text_message, &sender);
                                self.schedule_message_delivery(recipient, text_message, event_loop);
                            }
                        }
                    }
                    Message::MessageAcknowledgement(ack) => {
                        self.pending_deliveries.remove(&ack.message_id).map(|p| event_loop.clear_timeout(p));

                        if let None = self.acknowledgements.insert(ack.message_id, ack.clone()) {
                            if recipient == self.address {
                                debug!("Received acknowledgement for my message {:?} via {:?}", &ack.message_id, &sender);
                                self.broadcast_event(Event::ReceivedMessageAcknowledgement(ack));
                            } else {
                                debug!("Received acknowledgement for {}'s message {:?} via {:?}", &recipient, &ack.message_id, &sender);
                                self.deliver_acknowledgement(recipient, ack, event_loop);
                            }
                        }
                    }
                }
            }

            network::Event::Shutdown => {
                debug!("Received Shutdown signal from Network");
                event_loop.shutdown();
                self.broadcast_event(Event::Shutdown);
            }

            network::Event::Started => {
                debug!("Received Started signal from Network");
                self.broadcast_event(Event::Started);
            }
        }
    }

    fn schedule_message_delivery(&mut self, recipient: Address, text_message: TextMessage, event_loop: &mut mio::EventLoop<Client>) {
        let message_id = text_message.id;
        if !self.pending_deliveries.contains_key(&message_id) {
            let delivered = self.delivered.entry(message_id).or_insert(0);
            let delay = (2u64.pow(*delivered as u32) - 1) * 1000;
            debug!("Scheduling delivery of {} in {}ms", &message_id, delay);
            let timeout = event_loop.timeout_ms(ScheduledTask::DeliverMessage(recipient, text_message.clone()), delay).unwrap();
            self.pending_deliveries.insert(message_id, timeout);
            *delivered += 1;
        }
    }

    fn deliver_acknowledgement(&mut self, recipient: Address, acknowledgement: MessageAcknowledgement, _event_loop: &mut mio::EventLoop<Client>) {
        if let Some(ref commands) = self.network_commands {
            debug!("Delivering acknowledgement {:?} to {}", &acknowledgement, &recipient);
            let envelope = acknowledgement.envelope(recipient);
            commands.send(network::OneshotTask::SendPacket(recipient, envelope.encode())).unwrap();
        }
    }

    fn deliver_message(&mut self, recipient: Address, text_message: TextMessage, event_loop: &mut mio::EventLoop<Client>) {
        let delivered = self.network_commands.as_ref().map(|commands| {
            debug!("Delivering message {:?} to {}", &text_message, &recipient);
            let envelope = text_message.clone().envelope(recipient);
            commands.send(network::OneshotTask::SendPacket(recipient, envelope.encode()))
        }).unwrap();

        if let Ok(_) = delivered {
            debug!("Adding message {:?} to pending deliveries to be retried", &text_message);
            self.pending_deliveries.remove(&text_message.id);
            self.schedule_message_delivery(recipient, text_message.clone(), event_loop);
            self.broadcast_event(Event::SentTextMessage(text_message));
        }
    }

    fn broadcast_event(&self, event: Event) {
        for listener in self.event_listeners.iter() {
            listener.send(event.clone()).expect("Could not broadcast event");
        }
    }

    fn shutdown(&self, _event_loop: &mut mio::EventLoop<Client>) {
        debug!("Received Shutdown command from user");
        if let Some(ref commands) = self.network_commands {
            commands.send(network::OneshotTask::Shutdown).unwrap();
        }
    }
}

impl mio::Handler for Client {
    type Timeout = ScheduledTask;
    type Message = Task;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Client>, task: Task) {
        match task {
            Task::HandleNetworkEvent(event) => self.handle_networking_event(event, event_loop),
            Task::ScheduleMessageDelivery(recipient, message) => self.schedule_message_delivery(recipient, message, event_loop),
            Task::Shutdown => self.shutdown(event_loop)
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Client>, task: ScheduledTask) {
        match task {
            ScheduledTask::DeliverMessage(recipient, message) => self.deliver_message(recipient, message, event_loop)
        }
    }
}
<|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/. */

use std::collections::HashSet;
use std::slice::Iter;

// A device name can never be longer than 29 bytes. An adv packet is at most
// 31 bytes long. The length and identifier of the length field take 2 bytes.
// That leaves 29 bytes for the name.
const MAX_NAME_LENGTH: usize = 29;

#[derive(Deserialize, Serialize)]
pub struct ServiceUUIDSequence(Vec<String>);

impl ServiceUUIDSequence {
    pub fn new(vec: Vec<String>) -> ServiceUUIDSequence {
        ServiceUUIDSequence(vec)
    }

    fn get_services_set(&self) -> HashSet<String> {
        let mut set = HashSet::new();
        for s in self.0.iter() {
            set.insert(s.clone());
        }
        set
    }
}

#[derive(Deserialize, Serialize)]
pub struct BluetoothScanfilter {
    name: String,
    name_prefix: String,
    services: ServiceUUIDSequence,
}

impl BluetoothScanfilter {
    pub fn new(name: String, name_prefix: String, services: Vec<String>) -> BluetoothScanfilter {
        BluetoothScanfilter {
            name: name,
            name_prefix: name_prefix,
            services: ServiceUUIDSequence::new(services),
        }
    }

    pub fn get_name(&self) -> &str {
        &self.name
    }

    pub fn get_name_prefix(&self) -> &str {
        &self.name_prefix
    }

    pub fn get_services(&self) -> &[String] {
        &self.services.0
    }

    pub fn is_empty_or_invalid(&self) -> bool {
        (self.name.is_empty() && self.name_prefix.is_empty() && self.get_services().is_empty()) ||
        self.name.len() > MAX_NAME_LENGTH ||
        self.name_prefix.len() > MAX_NAME_LENGTH
    }
}

#[derive(Deserialize, Serialize)]
pub struct BluetoothScanfilterSequence(Vec<BluetoothScanfilter>);

impl BluetoothScanfilterSequence {
    pub fn new(vec: Vec<BluetoothScanfilter>) -> BluetoothScanfilterSequence {
        BluetoothScanfilterSequence(vec)
    }

    pub fn has_empty_or_invalid_filter(&self) -> bool {
        self.0.is_empty() ||
        self.0.iter().any(BluetoothScanfilter::is_empty_or_invalid)
    }

    pub fn iter(&self) -> Iter<BluetoothScanfilter> {
        self.0.iter()
    }

    fn get_services_set(&self) -> HashSet<String> {
        let mut set = HashSet::new();
        for filter in self.iter() {
            set = &set | &filter.services.get_services_set();
        }
        set
    }
}

#[derive(Deserialize, Serialize)]
pub struct RequestDeviceoptions {
    filters: BluetoothScanfilterSequence,
    optional_services: ServiceUUIDSequence,
}

impl RequestDeviceoptions {
    pub fn new(filters: BluetoothScanfilterSequence,
               services: ServiceUUIDSequence)
               -> RequestDeviceoptions {
        RequestDeviceoptions {
            filters: filters,
            optional_services: services,
        }
    }

    pub fn get_filters(&self) -> &BluetoothScanfilterSequence {
        &self.filters
    }

    pub fn get_services_set(&self) -> HashSet<String> {
        &self.filters.get_services_set() | &self.optional_services.get_services_set()
    }
}
<commit_msg>Auto merge of #11635 - Ms2ger:collect, r=nox<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/. */

use std::collections::HashSet;
use std::slice::Iter;

// A device name can never be longer than 29 bytes. An adv packet is at most
// 31 bytes long. The length and identifier of the length field take 2 bytes.
// That leaves 29 bytes for the name.
const MAX_NAME_LENGTH: usize = 29;

#[derive(Deserialize, Serialize)]
pub struct ServiceUUIDSequence(Vec<String>);

impl ServiceUUIDSequence {
    pub fn new(vec: Vec<String>) -> ServiceUUIDSequence {
        ServiceUUIDSequence(vec)
    }

    fn get_services_set(&self) -> HashSet<String> {
        self.0.iter().map(String::clone).collect()
    }
}

#[derive(Deserialize, Serialize)]
pub struct BluetoothScanfilter {
    name: String,
    name_prefix: String,
    services: ServiceUUIDSequence,
}

impl BluetoothScanfilter {
    pub fn new(name: String, name_prefix: String, services: Vec<String>) -> BluetoothScanfilter {
        BluetoothScanfilter {
            name: name,
            name_prefix: name_prefix,
            services: ServiceUUIDSequence::new(services),
        }
    }

    pub fn get_name(&self) -> &str {
        &self.name
    }

    pub fn get_name_prefix(&self) -> &str {
        &self.name_prefix
    }

    pub fn get_services(&self) -> &[String] {
        &self.services.0
    }

    pub fn is_empty_or_invalid(&self) -> bool {
        (self.name.is_empty() && self.name_prefix.is_empty() && self.get_services().is_empty()) ||
        self.name.len() > MAX_NAME_LENGTH ||
        self.name_prefix.len() > MAX_NAME_LENGTH
    }
}

#[derive(Deserialize, Serialize)]
pub struct BluetoothScanfilterSequence(Vec<BluetoothScanfilter>);

impl BluetoothScanfilterSequence {
    pub fn new(vec: Vec<BluetoothScanfilter>) -> BluetoothScanfilterSequence {
        BluetoothScanfilterSequence(vec)
    }

    pub fn has_empty_or_invalid_filter(&self) -> bool {
        self.0.is_empty() ||
        self.0.iter().any(BluetoothScanfilter::is_empty_or_invalid)
    }

    pub fn iter(&self) -> Iter<BluetoothScanfilter> {
        self.0.iter()
    }

    fn get_services_set(&self) -> HashSet<String> {
        self.iter().flat_map(|filter| filter.services.get_services_set()).collect()
    }
}

#[derive(Deserialize, Serialize)]
pub struct RequestDeviceoptions {
    filters: BluetoothScanfilterSequence,
    optional_services: ServiceUUIDSequence,
}

impl RequestDeviceoptions {
    pub fn new(filters: BluetoothScanfilterSequence,
               services: ServiceUUIDSequence)
               -> RequestDeviceoptions {
        RequestDeviceoptions {
            filters: filters,
            optional_services: services,
        }
    }

    pub fn get_filters(&self) -> &BluetoothScanfilterSequence {
        &self.filters
    }

    pub fn get_services_set(&self) -> HashSet<String> {
        &self.filters.get_services_set() | &self.optional_services.get_services_set()
    }
}
<|endoftext|>


<commit_before>extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;
use std::fs::File;
use std::io::Read;

fn reverse<T>(input: &mut [T], mut start: usize, mut len: usize) {
    loop {
        let a = start % input.len();
        let b = (start + len - 1) % input.len();
        input.swap(a, b);
        start += 1;
        if len <= 2 {
            break;
        } else {
            len -= 2;
        }
    }
}

fn shuffle<T>(input: &mut [T], lengths: &[usize]) {
    let mut position = 0;
    for (skip, length) in lengths.iter().enumerate() {
        reverse(input, position, *length);
        position += length + skip;
    }
}

fn part1(input: &str) -> Vec<u8> {
    let mut output: Vec<u8> = (0..256u16).map(|i| i as u8).collect();
    let lengths: Vec<usize> = input
        .trim()
        .split(',')
        .map(|val| val.parse().expect(&format!("could not parse {}", val)))
        .collect();
    shuffle(&mut output, &lengths);
    output
}

fn part2(input: &str) -> Vec<u8> {
    let mut lengths = input.trim().as_bytes().to_owned();
    lengths.append(&mut vec![17, 31, 73, 47, 23]);
    let repeated: Vec<usize> = lengths
        .iter()
        .cycle()
        .map(|i| *i as usize)
        .take(lengths.len() * 64)
        .collect();
    let mut output: Vec<u8> = (0..256u16).map(|i| i as u8).collect();
    // get sparse hash
    shuffle(&mut output, &repeated);
    output
        .chunks(16)
        .map(|chunk| chunk.iter().fold(0, |n, m| n ^ m))
        .collect()
}

fn as_hex(input: &[u8]) -> String {
    let mut s = String::with_capacity(input.len());
    for elem in input {
        s.push_str(&format!("{:02x}", elem));
    }
    s
}

fn main() {
    let opt = Opt::from_args();
    let mut contents = String::new();
    if opt.input == "-" {
        std::io::stdin()
            .read_to_string(&mut contents)
            .expect("could not read stdin");
    } else {
        let mut file = File::open(&opt.input)
            .expect(&format!("file {} not found", opt.input));
        file.read_to_string(&mut contents)
            .expect(&format!("could not read file {}", opt.input));
    }
    let shuffled = part1(&contents);
    println!(
        "Part 1: {}*{} = {}",
        shuffled[0],
        shuffled[1],
        shuffled[0] as usize * shuffled[1] as usize
    );
    println!("Part 2: {}", as_hex(&part2(&contents)));
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day10", about = "Advent of code 2017 day 10")]
struct Opt {
    #[structopt(help = "Input file")] input: String,
}

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

    #[test]
    fn shuffle_test() {
        let mut input = vec![0, 1, 2, 3, 4];
        let lengths = vec![3, 4, 1, 5];
        shuffle(&mut input, &lengths);
        assert_eq!(input, vec![3, 4, 2, 1, 0])
    }

    #[test]
    fn reverse_test() {
        let mut a = vec![0, 1, 2, 3, 4];
        reverse(&mut a, 1, 4);
        assert_eq!(a, vec![0, 4, 3, 2, 1]);
        let mut b = vec![0, 1, 2, 3, 4];
        reverse(&mut b, 5, 3);
        assert_eq!(b, vec![2, 1, 0, 3, 4]);
    }

    #[test]
    fn part2_test() {
        assert_eq!(as_hex(&part2("")), "a2582a3a0e66e6e86e3812dcb672a272");
        assert_eq!(
            as_hex(&part2("AoC 2017")),
            "33efeb34ea91902bb2f59c9920caa6cd"
        );
        assert_eq!(as_hex(&part2("1,2,3")), "3efbe78a8d82f29979031a4aa0b16a9d");
        assert_eq!(as_hex(&part2("1,2,4")), "63960835bcdc130f0b66d7ff4f6a5a8e");
    }
}
<commit_msg>Minor optimizations to 2017 day 10<commit_after>extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;
use std::fs::File;
use std::io::Read;

fn reverse<T>(input: &mut [T], mut start: usize, mut len: usize) {
    loop {
        let a = start % input.len();
        let b = (start + len - 1) % input.len();
        input.swap(a, b);
        start += 1;
        if len <= 2 {
            break;
        } else {
            len -= 2;
        }
    }
}

fn shuffle<T, Iter>(input: &mut [T], lengths: Iter)
    where Iter: IntoIterator<Item = usize>
{
    let mut position = 0;
    for (skip, length) in lengths.into_iter().enumerate() {
        reverse(input, position, length);
        position += length + skip;
    }
}

fn part1(input: &str) -> Vec<u8> {
    let mut output: Vec<u8> = (0..256u16).map(|i| i as u8).collect();
    let lengths: Vec<usize> = input
        .trim()
        .split(',')
        .map(|val| val.parse().expect(&format!("could not parse {}", val)))
        .collect();
    shuffle(&mut output, lengths);
    output
}

fn part2(input: &str) -> Vec<u8> {
    let mut output: Vec<u8> = (0..256u16).map(|i| i as u8).collect();
    let mut lengths = input.trim().as_bytes().to_owned();
    lengths.extend(vec![17, 31, 73, 47, 23]);
    let repeated = lengths
        .iter()
        .cycle()
        .map(|i| *i as usize)
        .take(lengths.len() * 64);
    // get sparse hash
    shuffle(&mut output, repeated);
    output
        .chunks(16)
        .map(|chunk| chunk.iter().fold(0, |n, m| n ^ m))
        .collect()
}

fn as_hex(input: &[u8]) -> String {
    let mut s = String::with_capacity(input.len() * 2);
    for elem in input {
        s.push_str(&format!("{:02x}", elem));
    }
    s
}

fn main() {
    let opt = Opt::from_args();
    let mut contents = String::new();
    if opt.input == "-" {
        std::io::stdin()
            .read_to_string(&mut contents)
            .expect("could not read stdin");
    } else {
        let mut file = File::open(&opt.input)
            .expect(&format!("file {} not found", opt.input));
        file.read_to_string(&mut contents)
            .expect(&format!("could not read file {}", opt.input));
    }
    let shuffled = part1(&contents);
    println!(
        "Part 1: {}*{} = {}",
        shuffled[0],
        shuffled[1],
        shuffled[0] as usize * shuffled[1] as usize
    );
    println!("Part 2: {}", as_hex(&part2(&contents)));
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day10", about = "Advent of code 2017 day 10")]
struct Opt {
    #[structopt(help = "Input file")] input: String,
}

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

    #[test]
    fn shuffle_test() {
        let mut input = vec![0, 1, 2, 3, 4];
        let lengths = vec![3, 4, 1, 5];
        shuffle(&mut input, lengths);
        assert_eq!(input, vec![3, 4, 2, 1, 0])
    }

    #[test]
    fn reverse_test() {
        let mut a = vec![0, 1, 2, 3, 4];
        reverse(&mut a, 1, 4);
        assert_eq!(a, vec![0, 4, 3, 2, 1]);
        let mut b = vec![0, 1, 2, 3, 4];
        reverse(&mut b, 5, 3);
        assert_eq!(b, vec![2, 1, 0, 3, 4]);
    }

    #[test]
    fn part2_test() {
        assert_eq!(as_hex(&part2("")), "a2582a3a0e66e6e86e3812dcb672a272");
        assert_eq!(
            as_hex(&part2("AoC 2017")),
            "33efeb34ea91902bb2f59c9920caa6cd"
        );
        assert_eq!(as_hex(&part2("1,2,3")), "3efbe78a8d82f29979031a4aa0b16a9d");
        assert_eq!(as_hex(&part2("1,2,4")), "63960835bcdc130f0b66d7ff4f6a5a8e");
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;

#[allow(unused_imports)]
use glium::glutin;
use crate::glutin::dpi::PhysicalSize;

fn main() {
    let event_loop = glium::glutin::event_loop::EventLoop::new();
    let cb = glutin::ContextBuilder::new();
    let size = PhysicalSize {
        width: 800,
        height: 600,
    };
    let context = cb.build_headless(&event_loop, size).unwrap();
    let context = unsafe {
        context.treat_as_current()
    };
    let display = glium::backend::glutin::headless::Headless::new(context).unwrap();

    let program = glium::program::ComputeShader::from_source(&display, r#"\

            #version 430
            layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

            layout(std140) buffer MyBlock {
                float power;
                vec4 values[4096/4];
            };

            void main() {
                vec4 val = values[gl_GlobalInvocationID.x];

                values[gl_GlobalInvocationID.x] = pow(val, vec4(power));
            }

        "#).unwrap();

    const NUM_VALUES: usize = 4096;

    #[repr(C)]
    #[derive(Clone, Copy)]
    struct Data {
        power: f32,
        _padding: [f32; 3],
        values: [[f32;4]; NUM_VALUES],
    }

    implement_uniform_block!(Data, power, values);

    let mut buffer: glium::uniforms::UniformBuffer<Data> =
              glium::uniforms::UniformBuffer::empty(&display).unwrap();

    {
        let mut mapping = buffer.map();
        mapping.power = rand::random();
        for val in mapping.values.iter_mut() {
            *val = [rand::random::<f32>(),rand::random::<f32>(),rand::random::<f32>(),rand::random::<f32>()];
        }
    }

    program.execute(uniform! { MyBlock: &*buffer }, 4096, 1, 1);

    {
        let mapping = buffer.map();
        println!("Power is: {:?}", mapping.power);
        for val in mapping.values.iter().take(3) {
            println!("{:?}", val[0]);
            println!("{:?}", val[1]);
            println!("{:?}", val[2]);
            println!("{:?}", val[3]);
        }
        println!("...");
    }
}
<commit_msg>Fix UniformBlockLayoutMismatch error in gpgpu example<commit_after>#[macro_use]
extern crate glium;

#[allow(unused_imports)]
use glium::glutin;
use crate::glutin::dpi::PhysicalSize;

fn main() {
    let event_loop = glium::glutin::event_loop::EventLoop::new();
    let cb = glutin::ContextBuilder::new();
    let size = PhysicalSize {
        width: 800,
        height: 600,
    };
    let context = cb.build_headless(&event_loop, size).unwrap();
    let context = unsafe {
        context.treat_as_current()
    };
    let display = glium::backend::glutin::headless::Headless::new(context).unwrap();

    let program = glium::program::ComputeShader::from_source(&display, r#"\

            #version 430
            layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

            layout(std140) buffer MyBlock {
                float power;
                vec4 values[4096/4];
            };

            void main() {
                vec4 val = values[gl_GlobalInvocationID.x];

                values[gl_GlobalInvocationID.x] = pow(val, vec4(power));
            }

        "#).unwrap();

    const NUM_VALUES: usize = 4096;

    #[repr(C)]
    #[derive(Clone, Copy)]
    struct Data {
        power: f32,
        _padding: [f32; 3],
        values: [[f32; 4]; NUM_VALUES / 4],
    }

    implement_uniform_block!(Data, power, values);

    let mut buffer: glium::uniforms::UniformBuffer<Data> =
              glium::uniforms::UniformBuffer::empty(&display).unwrap();

    {
        let mut mapping = buffer.map();
        mapping.power = rand::random();
        for val in mapping.values.iter_mut() {
            *val = [rand::random::<f32>(),rand::random::<f32>(),rand::random::<f32>(),rand::random::<f32>()];
        }
    }

    program.execute(uniform! { MyBlock: &*buffer }, NUM_VALUES as u32 / 4, 1, 1);

    {
        let mapping = buffer.map();
        println!("Power is: {:?}", mapping.power);
        for val in mapping.values.iter().take(3) {
            println!("{:?}", val[0]);
            println!("{:?}", val[1]);
            println!("{:?}", val[2]);
            println!("{:?}", val[3]);
        }
        println!("...");
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};

async fn hello(_: Request<Body>) -> Result<Response<Body>, hyper::Error> {
    Ok(Response::new(Body::from("Hello World!")))
}

#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    pretty_env_logger::init();

    let addr = ([127, 0, 0, 1], 3000).into();

    let server = Server::bind(&addr)
        .serve(make_service_fn(|_| {
            // This is the `Service` that will handle the connection.
            // `service_fn` is a helper to convert a function that
            // returns a Response into a `Service`.
            async {
                Ok::<_, hyper::Error>(service_fn(hello))
            }
        }));

    println!("Listening on http://{}", addr);

    server.await?;

    Ok(())
}
<commit_msg>docs(examples): add more comments to hello server example<commit_after>#![deny(warnings)]

use std::convert::Infallible;

use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};

async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
    Ok(Response::new(Body::from("Hello World!")))
}

#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    pretty_env_logger::init();

    // For every connection, we must make a `Service` to handle all
    // incoming HTTP requests on said connection.
    let make_svc = make_service_fn(|_conn| {
        // This is the `Service` that will handle the connection.
        // `service_fn` is a helper to convert a function that
        // returns a Response into a `Service`.
        async {
            Ok::<_, Infallible>(service_fn(hello))
        }
    });

    let addr = ([127, 0, 0, 1], 3000).into();

    let server = Server::bind(&addr).serve(make_svc);

    println!("Listening on http://{}", addr);

    server.await?;

    Ok(())
}
<|endoftext|>


<commit_before>use super::values::*;
use std::rc::*;

pub use super::treenode_sugar::*;
pub use super::treenode_index::*;

///
/// The treenode trait is implemented by types that can act as part of a tree
///
pub trait TreeNode {
    ///
    /// Retrieves a reference to the child of this tree node (or None if this node has no child)
    ///
    fn get_child_ref(&self) -> Option<&Rc<TreeNode>>;

    ///
    /// Retrieves a reference to the sibling of this tree node (or None if this node has no sibling)
    ///
    fn get_sibling_ref(&self) -> Option<&Rc<TreeNode>>;

    ///
    /// Retrieves the tag attached to this tree node
    ///
    fn get_tag(&self) -> &str;

    ///
    /// Retrieves the value attached to this node
    ///
    fn get_value(&self) -> &TreeValue;
}

///
/// This trait is implemented by types that can be converted into a tree node.
///
pub trait ToTreeNode {
    ///
    /// Converts this value into a tree node
    ///
    fn to_tree_node(&self) -> Rc<TreeNode>;
}

///
/// This trait is implemented by tree nodes that can be changed
///
pub trait MutableTreeNode : TreeNode {
    ///
    /// Sets the child for this tree node
    ///
    fn set_child_ref(&mut self, new_node: Rc<TreeNode>);

    ///
    /// Sets the sibling for this tree node
    ///
    fn set_sibling_ref(&mut self, new_node: Rc<TreeNode>);

    ///
    /// Unsets the child for this node
    ///
    fn clear_child(&mut self);

    ///
    /// Unsets the sibling for this node
    ///
    fn clear_sibling(&mut self);

    ///
    /// Changes the value set for this node.
    ///
    fn set_tree_value(&mut self, new_value: TreeValue);

    ///
    /// Changes the tag attached to this tree
    ///
    fn set_tag(&mut self, new_tag: &str);
}

impl<T> ToTreeNode for T where T: TreeNode, T: Clone, T: 'static {
    ///
    /// Converts this value into a tree node
    ///
    fn to_tree_node(&self) -> Rc<TreeNode> {
        Rc::new(self.clone())
    }
}

impl ToTreeNode for Rc<TreeNode> {
    ///
    /// Converts this value into a tree node
    ///
    fn to_tree_node(&self) -> Rc<TreeNode> {
        self.clone()
    }
}
<commit_msg>Change ToTreeNode so that'll work for Rc<T: TreeNode> instead of just Rc<TreeNode><commit_after>use super::values::*;
use std::rc::*;

pub use super::treenode_sugar::*;
pub use super::treenode_index::*;

///
/// The treenode trait is implemented by types that can act as part of a tree
///
pub trait TreeNode {
    ///
    /// Retrieves a reference to the child of this tree node (or None if this node has no child)
    ///
    fn get_child_ref(&self) -> Option<&Rc<TreeNode>>;

    ///
    /// Retrieves a reference to the sibling of this tree node (or None if this node has no sibling)
    ///
    fn get_sibling_ref(&self) -> Option<&Rc<TreeNode>>;

    ///
    /// Retrieves the tag attached to this tree node
    ///
    fn get_tag(&self) -> &str;

    ///
    /// Retrieves the value attached to this node
    ///
    fn get_value(&self) -> &TreeValue;
}

///
/// This trait is implemented by types that can be converted into a tree node.
///
pub trait ToTreeNode {
    ///
    /// Converts this value into a tree node
    ///
    fn to_tree_node(&self) -> Rc<TreeNode>;
}

///
/// This trait is implemented by tree nodes that can be changed
///
pub trait MutableTreeNode : TreeNode {
    ///
    /// Sets the child for this tree node
    ///
    fn set_child_ref(&mut self, new_node: Rc<TreeNode>);

    ///
    /// Sets the sibling for this tree node
    ///
    fn set_sibling_ref(&mut self, new_node: Rc<TreeNode>);

    ///
    /// Unsets the child for this node
    ///
    fn clear_child(&mut self);

    ///
    /// Unsets the sibling for this node
    ///
    fn clear_sibling(&mut self);

    ///
    /// Changes the value set for this node.
    ///
    fn set_tree_value(&mut self, new_value: TreeValue);

    ///
    /// Changes the tag attached to this tree
    ///
    fn set_tag(&mut self, new_tag: &str);
}

impl<T> ToTreeNode for T where T: TreeNode, T: Clone, T: 'static {
    ///
    /// Converts this value into a tree node
    ///
    fn to_tree_node(&self) -> Rc<TreeNode> {
        Rc::new(self.clone())
    }
}

impl<T> ToTreeNode for Rc<T> where T: TreeNode, T: 'static {
    ///
    /// Converts this value into a tree node
    ///
    fn to_tree_node(&self) -> Rc<TreeNode> {
        self.clone()
    }
}

impl<'a, T> ToTreeNode for &'a Rc<T> where T: TreeNode, T: 'static {
    ///
    /// Converts this value into a tree node
    ///
    fn to_tree_node(&self) -> Rc<TreeNode> {
        (*self).clone()
    }
}

<|endoftext|>


<commit_before>use anyhow::{Context, Result};
use log::trace;
use std::path::Path;
use std::slice::Iter;
use tokio::fs::File;
use tokio::io::AsyncBufReadExt;
use tokio::io::BufReader;

use crate::{IntoName, Name};

static DEFAULT_WORD_LIST: &str = include_str!("../../../../contrib/subdomains-top1mil-5000.txt");

#[derive(Debug)]
pub struct Wordlist {
    words: Vec<Name>,
}

impl Wordlist {
    pub async fn from_file<P: AsRef<Path>>(path: P) -> Result<Wordlist> {
        let file = File::open(path).await?;
        let mut buf_reader = BufReader::new(file);

        let mut words = Vec::new();
        loop {
            let mut buffer = String::new();
            let len = buf_reader.read_line(&mut buffer).await?;
            if len == 0 {
                break;
            }
            if Wordlist::is_comment(&buffer) {
                continue;
            }
            trace!("Parsing wordlist item '{}'.", buffer);
            let name: Name = buffer
                .trim_end() // BufReader::read_line returns trainling line break
                .into_name()
                .context("failed to read word list because of invalid domain name")?;
            words.push(name);
        }

        Ok(Wordlist { words })
    }

    fn is_comment(line: &str) -> bool {
        line.starts_with("//") || line.starts_with("#")
    }

    pub fn from_str(data: &str) -> Result<Wordlist> {
        let mut words = Vec::new();

        for line in data.lines() {
            if Wordlist::is_comment(&line) {
                continue;
            }
            trace!("Parsing wordlist item '{}'.", line);
            let name: Name = line
                .into_name()
                .context("failed to read word list because of invalid domain name")?;
            words.push(name);
        }

        Ok(Wordlist { words })
    }

    pub fn default() -> Result<Wordlist> {
        Wordlist::from_str(DEFAULT_WORD_LIST)
    }

    #[allow(dead_code)]
    pub fn iter(&self) -> Iter<Name> {
        self.words.iter()
    }

    #[allow(dead_code)]
    pub fn len(&self) -> usize {
        self.words.len()
    }
}

impl IntoIterator for Wordlist {
    type Item = Name;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.words.into_iter()
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    use spectral::prelude::*;

    #[tokio::test]
    async fn read_from_file_5000() {
        let path = "contrib/subdomains-top1mil-5000.txt";

        let wordlist = Wordlist::from_file(path).await;

        asserting("Wordlist with 5000 elements loaded from file")
            .that(&wordlist)
            .is_ok()
            .map(|x| &x.words)
            .has_length(5000)
    }

    #[tokio::test]
    async fn read_from_file_20000() {
        let path = "contrib/subdomains-top1mil-20000.txt";

        let wordlist = Wordlist::from_file(path).await;

        asserting("Wordlist with 20000 elements loaded from file")
            .that(&wordlist)
            .is_ok()
            .map(|x| &x.words)
            .has_length(19998)
    }

    #[test]
    fn read_from_string() {
        let wordlist = Wordlist::from_str(&DEFAULT_WORD_LIST);

        asserting("Wordlist with 5000 elements loaded from string")
            .that(&wordlist)
            .is_ok()
            .map(|x| &x.words)
            .has_length(5000)
    }
}
<commit_msg>Reduce built in Wordlist<commit_after>use anyhow::{Context, Result};
use log::trace;
use std::path::Path;
use std::slice::Iter;
use tokio::fs::File;
use tokio::io::AsyncBufReadExt;
use tokio::io::BufReader;

use crate::{IntoName, Name};

static DEFAULT_WORD_LIST: &str = r#"admin
alpha
api
app
assets
auth
beta
blog
chat
cms
confluence
ct
dashboard
dev
development
dialin
download
epaper
exchange
external
foto
ftp
gatekeeper
gateway
git
gitlab
gw
hq
id
in
intern
iphone
jobs
ldap
listserv
login
m
mail
mailhost
meet
mobi
mx
newsletter
ns
presse
public
radio
redirector
relay
reporting
rss
sales
sftp
shop
smtp
smtp01
smtp02
smtp1
smtp2
special
sso
staging
static
svn
test
ticket
tp
tr
update
upload
web
www
www-server
www-test
wwwtest"#;

#[derive(Debug)]
pub struct Wordlist {
    words: Vec<Name>,
}

impl Wordlist {
    pub async fn from_file<P: AsRef<Path>>(path: P) -> Result<Wordlist> {
        let file = File::open(path).await?;
        let mut buf_reader = BufReader::new(file);

        let mut words = Vec::new();
        loop {
            let mut buffer = String::new();
            let len = buf_reader.read_line(&mut buffer).await?;
            if len == 0 {
                break;
            }
            if Wordlist::is_comment(&buffer) {
                continue;
            }
            trace!("Parsing wordlist item '{}'.", buffer);
            let name: Name = buffer
                .trim_end() // BufReader::read_line returns trainling line break
                .into_name()
                .context("failed to read word list because of invalid domain name")?;
            words.push(name);
        }

        Ok(Wordlist { words })
    }

    fn is_comment(line: &str) -> bool {
        line.starts_with("//") || line.starts_with("#")
    }

    pub fn from_str(data: &str) -> Result<Wordlist> {
        let mut words = Vec::new();

        for line in data.lines() {
            if Wordlist::is_comment(&line) {
                continue;
            }
            trace!("Parsing wordlist item '{}'.", line);
            let name: Name = line
                .into_name()
                .context("failed to read word list because of invalid domain name")?;
            words.push(name);
        }

        Ok(Wordlist { words })
    }

    pub fn default() -> Result<Wordlist> {
        Wordlist::from_str(DEFAULT_WORD_LIST)
    }

    #[allow(dead_code)]
    pub fn iter(&self) -> Iter<Name> {
        self.words.iter()
    }

    #[allow(dead_code)]
    pub fn len(&self) -> usize {
        self.words.len()
    }
}

impl IntoIterator for Wordlist {
    type Item = Name;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.words.into_iter()
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    use spectral::prelude::*;

    #[tokio::test]
    async fn read_from_file_5000() {
        let path = "contrib/subdomains-top1mil-5000.txt";

        let wordlist = Wordlist::from_file(path).await;

        asserting("Wordlist with 5000 elements loaded from file")
            .that(&wordlist)
            .is_ok()
            .map(|x| &x.words)
            .has_length(5000)
    }

    #[tokio::test]
    async fn read_from_file_20000() {
        let path = "contrib/subdomains-top1mil-20000.txt";

        let wordlist = Wordlist::from_file(path).await;

        asserting("Wordlist with 20000 elements loaded from file")
            .that(&wordlist)
            .is_ok()
            .map(|x| &x.words)
            .has_length(19998)
    }

    #[test]
    fn read_from_string() {
        let wordlist = Wordlist::from_str(&DEFAULT_WORD_LIST);

        asserting("Wordlist with 5000 elements loaded from string")
            .that(&wordlist)
            .is_ok()
            .map(|x| &x.words)
            .has_length(5000)
    }
}
<|endoftext|>


<commit_before>// A corpus of cleaned text optimized for generating ngrams as string slices.
pub struct Corpus {
    text: String,
    wrap_around: String,
    ngram_length: usize,
}

impl Corpus {
    pub fn new(text: &str, ngram_length: usize, min_word_length: usize) -> Corpus {
        let text = clean_corpus(text, min_word_length);
        // TODO: handle unwrap failure.
        let (last_index, _) = text.char_indices().rev().nth(ngram_length - 1).unwrap();
        let mut wrap_around: String = text[last_index..].to_string();
        wrap_around.push_str(&text.chars().take(ngram_length).collect::<String>());

        Corpus {
            text: text,
            wrap_around: wrap_around,
            ngram_length: ngram_length,
        }
    }

    pub fn get_ngrams(&self) -> Vec<&str> {
        let ngrams = self
            .text
            .char_indices()
            .zip(self.text.char_indices().skip(self.ngram_length))
            .map(|((i, _), (j, _))| &self.text[i..j]);
        let wrap_ngrams = self
            .wrap_around
            .char_indices()
            .zip(self.wrap_around.char_indices().skip(self.ngram_length))
            .map(|((i, _), (j, _))| &self.wrap_around[i..j]);

        ngrams.chain(wrap_ngrams).collect()
    }
}

fn clean_corpus(text: &str, min_word_length: usize) -> String {
    let text = text.to_lowercase();
    let words = text
        .split_whitespace()
        .filter_map(|word| clean_word(word, min_word_length));

    Some("")
        .into_iter()
        .chain(words)
        .collect::<Vec<&str>>()
        .join(" ")
}

fn clean_word(word: &str, min_length: usize) -> Option<&str> {
    let word = word.trim_matches(|c| !is_word_char(c));

    if word.chars().all(is_word_char) && word.len() >= min_length {
        Some(word)
    } else {
        None
    }
}

fn is_word_char(c: char) -> bool {
    c.is_alphabetic() || c == '\''
}

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

    #[test]
    fn test_clean_word() {
        assert_eq!(clean_word("Test", 3), Some("Test"));
        assert_eq!(clean_word("123test@314", 3), Some("test"));
        assert_eq!(clean_word("2#@test'in23", 3), Some("test'in"));
        assert_eq!(clean_word("31ld;Test", 3), None);
        assert_eq!(clean_word("a", 2), None);
        assert_eq!(clean_word("Test", 5), None);
    }

    #[test]
    fn test_clean_corpus() {
        assert_eq!(clean_corpus("this is a test", 3), " this test");
        assert_eq!(clean_corpus("Some awes0me test", 3), " some test");
        assert_eq!(clean_corpus("test'in", 3), " test'in");
        assert_eq!(clean_corpus("this is a test", 5), "");
    }

    #[test]
    fn test_get_ngrams() {
        let corpus = Corpus::new("this is a test", 3, 3);
        assert_eq!(
            corpus.get_ngrams(),
            vec![" th", "thi", "his", "is ", "s t", " te", "tes", "est", "st ", "t t"]
        );
        let corpus = Corpus::new("this is a test", 5, 3);
        assert_eq!(
            corpus.get_ngrams(),
            vec![
                " this", "this ", "his t", "is te", "s tes", " test", "test ", "est t", "st th",
                "t thi",
            ]
        );
        let corpus = Corpus::new("this is a test", 3, 2);
        assert_eq!(
            corpus.get_ngrams(),
            vec![
                " th", "thi", "his", "is ", "s i", " is", "is ", "s t", " te", "tes", "est", "st ",
                "t t",
            ]
        );
    }
}
<commit_msg>Add unwrap logic so that too small a corpus doesn't cause a panic.<commit_after>// A corpus of cleaned text with wrap around to allow string slices that wrap.
pub struct Corpus {
    text: String,
    wrap_around: String,
    ngram_length: usize,
}

impl Corpus {
    pub fn new(text: &str, ngram_length: usize, min_word_length: usize) -> Corpus {
        let text = clean_corpus(text, min_word_length);

        // Populate wrap_around from the end of the text and the start of the text.
        let wrap_around_index = text
            .char_indices()
            .rev()
            .nth(ngram_length - 1)
            .map(|(i, _)| i)
            .unwrap_or(0);
        let mut wrap_around: String = text[wrap_around_index..].to_string();
        wrap_around.push_str(&text.chars().take(ngram_length).collect::<String>());

        Corpus {
            text: text,
            wrap_around: wrap_around,
            ngram_length: ngram_length,
        }
    }

    pub fn get_ngrams(&self) -> Vec<&str> {
        let ngrams = self
            .text
            .char_indices()
            .zip(self.text.char_indices().skip(self.ngram_length))
            .map(|((i, _), (j, _))| &self.text[i..j]);
        let wrap_ngrams = self
            .wrap_around
            .char_indices()
            .zip(self.wrap_around.char_indices().skip(self.ngram_length))
            .map(|((i, _), (j, _))| &self.wrap_around[i..j]);

        ngrams.chain(wrap_ngrams).collect()
    }
}

fn clean_corpus(text: &str, min_word_length: usize) -> String {
    let text = text.to_lowercase();
    let words = text
        .split_whitespace()
        .filter_map(|word| clean_word(word, min_word_length));

    // Insert a space at the start of the corpus so that every word begins with a space.
    Some("")
        .into_iter()
        .chain(words)
        .collect::<Vec<&str>>()
        .join(" ")
}

fn clean_word(word: &str, min_length: usize) -> Option<&str> {
    let word = word.trim_matches(|c| !is_word_char(c));

    if word.chars().all(is_word_char) && word.len() >= min_length {
        Some(word)
    } else {
        None
    }
}

fn is_word_char(c: char) -> bool {
    c.is_alphabetic() || c == '\''
}

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

    #[test]
    fn test_clean_word() {
        assert_eq!(clean_word("Test", 3), Some("Test"));
        assert_eq!(clean_word("123test@314", 3), Some("test"));
        assert_eq!(clean_word("2#@test'in23", 3), Some("test'in"));
        assert_eq!(clean_word("31ld;Test", 3), None);
        assert_eq!(clean_word("a", 2), None);
        assert_eq!(clean_word("Test", 5), None);
    }

    #[test]
    fn test_clean_corpus() {
        assert_eq!(clean_corpus("this is a test", 3), " this test");
        assert_eq!(clean_corpus("Some awes0me test", 3), " some test");
        assert_eq!(clean_corpus("test'in", 3), " test'in");
        assert_eq!(clean_corpus("this is a test", 5), "");
    }

    #[test]
    fn test_get_ngrams() {
        let corpus = Corpus::new("this is a test", 3, 3);
        assert_eq!(
            corpus.get_ngrams(),
            vec![" th", "thi", "his", "is ", "s t", " te", "tes", "est", "st ", "t t"]
        );
        let corpus = Corpus::new("this is a test", 5, 3);
        assert_eq!(
            corpus.get_ngrams(),
            vec![
                " this", "this ", "his t", "is te", "s tes", " test", "test ", "est t", "st th",
                "t thi",
            ]
        );
        let corpus = Corpus::new("this is a test", 3, 2);
        assert_eq!(
            corpus.get_ngrams(),
            vec![
                " th", "thi", "his", "is ", "s i", " is", "is ", "s t", " te", "tes", "est", "st ",
                "t t",
            ]
        );
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use error::{Error, Result};

use std::path::{Path, PathBuf};
use std::io::Write;
use std::process::Command;
use std::fs::{self, File, DirBuilder};
use uuid;
use std::ops::Drop;

/// Struct used to create zip (using filesystem and zip command)
pub struct Zipper {
    args: Vec<String>,
    path: PathBuf,
}

impl Zipper {
    /// Creates new zipper
    ///
    /// # Arguments
    /// * `path`: the path to a temporary directory
    /// (zipper will create a random dir in it and clean it later)
    pub fn new(path: &str) -> Result<Zipper> {
        let uuid = uuid::Uuid::new_v4();
        let zipper_path = Path::new(path).join(uuid.simple().to_string());

        try!(DirBuilder::new()
            .recursive(true)
            .create(&zipper_path)
            .map_err(|_| {
                Error::zipper(lformat!("could not create temporary directory in {path}",
                                       path = path))
            }));

        Ok(Zipper {
            args: vec![],
            path: zipper_path,
        })
    }

    /// writes a content to a temporary file
    pub fn write(&mut self, file: &str, content: &[u8], add_args: bool) -> Result<()> {
        let path = Path::new(file);
        if path.starts_with("..") || path.is_absolute() {
            return Err(Error::zipper(lformat!("file {file} refers to an absolute or a parent \
                                               path.
This is forbidden because we are supposed \
                                               to create a temporary file in a temporary dir.",
                                              file = file)));
        }
        let dest_file = self.path.join(path);
        let dest_dir = dest_file.parent().unwrap();
        if !fs::metadata(dest_dir).is_ok() {
            // dir does not exist, create it
            try!(DirBuilder::new()
                .recursive(true)
                .create(&dest_dir)
                .map_err(|_| {
                    Error::zipper(lformat!("could not create temporary directory in {path}",
                                           path = dest_dir.display()))
                }));
        }

        if let Ok(mut f) = File::create(&dest_file) {
            if f.write_all(content).is_ok() {
                if add_args {
                    self.args.push(String::from(file));
                }
                Ok(())
            } else {
                Err(Error::zipper(lformat!("could not write to temporary file {file}",
                                           file = file)))
            }
        } else {
            Err(Error::zipper(lformat!("could not create temporary file {file}", file = file)))
        }
    }

    /// Unzip a file and deletes it afterwards
    pub fn unzip(&mut self, file: &str) -> Result<()> {
        let output = Command::new("unzip")
            .current_dir(&self.path)
            .arg(file)
            .output()
            .map_err(|e| {
                Error::zipper(lformat!("failed to execute unzip on {file}: {error}",
                                       file = file,
                                       error = e))
            });

        try!(output);

        fs::remove_file(self.path.join(file))
            .map_err(|_| Error::zipper(lformat!("failed to remove file {file}", file = file)))
    }

    /// run command and copy file name (supposed to result from the command) to current dir
    pub fn run_command(&mut self,
                       mut command: Command,
                       command_name: &str,
                       in_file: &str,
                       out_file: &str)
                       -> Result<String> {
        let res_output = command.args(&self.args)
            .current_dir(&self.path)
            .output()
            .map_err(|e| Error::zipper(lformat!("failed to run command {name}: {error}",
                                                name = command_name,
                                                error = e)));
        let output = try!(res_output);
        try!(fs::copy(self.path.join(in_file), out_file).map_err(|_| {
            println!("{}", &String::from_utf8_lossy(&output.stdout));
            Error::zipper(lformat!("could not copy file {input} to {output}",
                                   input = in_file,
                                   output = out_file))
        }));
        if output.status.success() {
            Ok(String::from_utf8_lossy(&output.stdout).into_owned())
        } else {
            Err(Error::zipper(lformat!("command didn't return succesfully: {output}",
                                       output = String::from_utf8_lossy(&output.stdout))))
        }
    }

    /// zip all files in zipper's tmp dir to a given file name and return odt file
    pub fn generate_odt(&mut self, command_name: &str, odt_file: &str) -> Result<String> {
        let mut command = Command::new(command_name);
        command.arg("-r");
        command.arg("result.odt");
        command.arg(".");
        self.run_command(command, command_name, "result.odt", odt_file)
    }

    /// generate a pdf file into given file name
    pub fn generate_pdf(&mut self,
                        command_name: &str,
                        tex_file: &str,
                        pdf_file: &str)
                        -> Result<String> {
        // first pass
        let _ = Command::new(command_name)
            .current_dir(&self.path)
            .arg(tex_file)
            .output();

        // second pass
        let mut command = Command::new(command_name);
        command.arg(tex_file);
        self.run_command(command, command_name, "result.pdf", pdf_file)
    }

    /// generate an epub into given file name
    pub fn generate_epub(&mut self, command_name: &str, file: &str) -> Result<String> {
        let mut command = Command::new(command_name);
        command.arg("-X");
        command.arg("result.epub");
        self.run_command(command, command_name, "result.epub", file)
    }
}

impl Drop for Zipper {
    fn drop(&mut self) {
        if let Err(err) = fs::remove_dir_all(&self.path) {
            println!("Error in zipper: could not delete temporary directory {}, error: {}",
                     self.path.to_string_lossy(),
                     err);
        }
    }
}
<commit_msg>Use ? in zipper.rs<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use error::{Error, Result};

use std::path::{Path, PathBuf};
use std::io::Write;
use std::process::Command;
use std::fs::{self, File, DirBuilder};
use uuid;
use std::ops::Drop;

/// Struct used to create zip (using filesystem and zip command)
pub struct Zipper {
    args: Vec<String>,
    path: PathBuf,
}

impl Zipper {
    /// Creates new zipper
    ///
    /// # Arguments
    /// * `path`: the path to a temporary directory
    /// (zipper will create a random dir in it and clean it later)
    pub fn new(path: &str) -> Result<Zipper> {
        let uuid = uuid::Uuid::new_v4();
        let zipper_path = Path::new(path).join(uuid.simple().to_string());

        DirBuilder::new()
            .recursive(true)
            .create(&zipper_path)
            .map_err(|_| {
                Error::zipper(lformat!("could not create temporary directory in {path}",
                                       path = path))
            })?;

        Ok(Zipper {
            args: vec![],
            path: zipper_path,
        })
    }

    /// writes a content to a temporary file
    pub fn write(&mut self, file: &str, content: &[u8], add_args: bool) -> Result<()> {
        let path = Path::new(file);
        if path.starts_with("..") || path.is_absolute() {
            return Err(Error::zipper(lformat!("file {file} refers to an absolute or a parent \
                                               path.
This is forbidden because we are supposed \
                                               to create a temporary file in a temporary dir.",
                                              file = file)));
        }
        let dest_file = self.path.join(path);
        let dest_dir = dest_file.parent().unwrap();
        if !fs::metadata(dest_dir).is_ok() {
            // dir does not exist, create it
            DirBuilder::new()
                .recursive(true)
                .create(&dest_dir)
                .map_err(|_| {
                    Error::zipper(lformat!("could not create temporary directory in {path}",
                                           path = dest_dir.display()))
                })?;
        }

        if let Ok(mut f) = File::create(&dest_file) {
            if f.write_all(content).is_ok() {
                if add_args {
                    self.args.push(String::from(file));
                }
                Ok(())
            } else {
                Err(Error::zipper(lformat!("could not write to temporary file {file}",
                                           file = file)))
            }
        } else {
            Err(Error::zipper(lformat!("could not create temporary file {file}", file = file)))
        }
    }

    /// Unzip a file and deletes it afterwards
    pub fn unzip(&mut self, file: &str) -> Result<()> {
        let output = Command::new("unzip")
            .current_dir(&self.path)
            .arg(file)
            .output()
            .map_err(|e| {
                Error::zipper(lformat!("failed to execute unzip on {file}: {error}",
                                       file = file,
                                       error = e))
            });

        output?;

        fs::remove_file(self.path.join(file))
            .map_err(|_| Error::zipper(lformat!("failed to remove file {file}", file = file)))
    }

    /// run command and copy file name (supposed to result from the command) to current dir
    pub fn run_command(&mut self,
                       mut command: Command,
                       command_name: &str,
                       in_file: &str,
                       out_file: &str)
                       -> Result<String> {
        let res_output = command.args(&self.args)
            .current_dir(&self.path)
            .output()
            .map_err(|e| Error::zipper(lformat!("failed to run command {name}: {error}",
                                                name = command_name,
                                                error = e)));
        let output = res_output?;
        fs::copy(self.path.join(in_file), out_file)
            .map_err(|_| {
                println!("{}", &String::from_utf8_lossy(&output.stdout));
                Error::zipper(lformat!("could not copy file {input} to {output}",
                                       input = in_file,
                                       output = out_file))
            })?;
        if output.status.success() {
            Ok(String::from_utf8_lossy(&output.stdout).into_owned())
        } else {
            Err(Error::zipper(lformat!("command didn't return succesfully: {output}",
                                       output = String::from_utf8_lossy(&output.stdout))))
        }
    }

    /// zip all files in zipper's tmp dir to a given file name and return odt file
    pub fn generate_odt(&mut self, command_name: &str, odt_file: &str) -> Result<String> {
        let mut command = Command::new(command_name);
        command.arg("-r");
        command.arg("result.odt");
        command.arg(".");
        self.run_command(command, command_name, "result.odt", odt_file)
    }

    /// generate a pdf file into given file name
    pub fn generate_pdf(&mut self,
                        command_name: &str,
                        tex_file: &str,
                        pdf_file: &str)
                        -> Result<String> {
        // first pass
        let _ = Command::new(command_name)
            .current_dir(&self.path)
            .arg(tex_file)
            .output();

        // second pass
        let mut command = Command::new(command_name);
        command.arg(tex_file);
        self.run_command(command, command_name, "result.pdf", pdf_file)
    }

    /// generate an epub into given file name
    pub fn generate_epub(&mut self, command_name: &str, file: &str) -> Result<String> {
        let mut command = Command::new(command_name);
        command.arg("-X");
        command.arg("result.epub");
        self.run_command(command, command_name, "result.epub", file)
    }
}

impl Drop for Zipper {
    fn drop(&mut self) {
        if let Err(err) = fs::remove_dir_all(&self.path) {
            println!("Error in zipper: could not delete temporary directory {}, error: {}",
                     self.path.to_string_lossy(),
                     err);
        }
    }
}
<|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/. */

use bincode::serde::serialize;
use bincode;
use std::mem;
use std::any::TypeId;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::ops::DerefMut;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use webrender_traits::ApiMsg;
use byteorder::{LittleEndian, WriteBytesExt};

lazy_static! {
    static ref WEBRENDER_RECORDING_DETOUR: Arc<Mutex<Option<Box<ApiRecordingReceiver>>>> = Arc::new(Mutex::new(None));
}

pub trait ApiRecordingReceiver: Send {
    fn write_msg(&mut self, frame: u32, msg: &ApiMsg);
    fn write_payload(&mut self, frame: u32, data: &[u8]);
}

pub fn set_recording_detour(detour: Option<Box<ApiRecordingReceiver>>) {
    let mut recorder = WEBRENDER_RECORDING_DETOUR.lock();
    *recorder.as_mut().unwrap().deref_mut() = detour;
}

fn write_data(frame: u32, data: &[u8]) {
    let filename = format!("record/frame_{}.bin", frame);
    let mut file = if !PathBuf::from(&filename).exists() {
        let mut file = File::create(filename).unwrap();

        let apimsg_type_id = unsafe {
            assert!(mem::size_of::<TypeId>() == mem::size_of::<u64>());
            mem::transmute::<TypeId, u64>(TypeId::of::<ApiMsg>())
        };
        file.write_u64::<LittleEndian>(apimsg_type_id).ok();
        file
    } else {
        OpenOptions::new().append(true).create(false).open(filename).unwrap()
    };
    file.write_u32::<LittleEndian>(data.len() as u32).ok();
    file.write(data).ok();
}

pub fn write_msg(frame: u32, msg: &ApiMsg) {
    match msg {
        &ApiMsg::AddRawFont(..) |
        &ApiMsg::AddNativeFont(..) |
        &ApiMsg::AddImage(..) |
        &ApiMsg::UpdateImage(..) |
        &ApiMsg::DeleteImage(..)|
        &ApiMsg::SetRootDisplayList(..) |
        &ApiMsg::SetRootPipeline(..) |
        &ApiMsg::Scroll(..) |
        &ApiMsg::TickScrollingBounce |
        &ApiMsg::WebGLCommand(..) => {
            let mut recorder = WEBRENDER_RECORDING_DETOUR.lock();
            if let Some(ref mut recorder) = recorder.as_mut().unwrap().as_mut() {
                recorder.write_msg(frame, &msg);
            } else {
                let buff = serialize(msg, bincode::SizeLimit::Infinite).unwrap();
                write_data(frame, &buff);
            }
       }
       _ => {}
    }
}

pub fn write_payload(frame: u32, data: &[u8]) {
    let mut recorder = WEBRENDER_RECORDING_DETOUR.lock();
    if let Some(ref mut recorder) = recorder.as_mut().unwrap().as_mut() {
        recorder.write_payload(frame, data);
    } else {
        write_data(frame, &[]); //signal the payload
        write_data(frame, data);
    }
}
<commit_msg>Drop unneeded Arc<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/. */

use bincode::serde::serialize;
use bincode;
use std::mem;
use std::any::TypeId;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::ops::DerefMut;
use std::path::PathBuf;
use std::sync::Mutex;
use webrender_traits::ApiMsg;
use byteorder::{LittleEndian, WriteBytesExt};

lazy_static! {
    static ref WEBRENDER_RECORDING_DETOUR: Mutex<Option<Box<ApiRecordingReceiver>>> = Mutex::new(None);
}

pub trait ApiRecordingReceiver: Send {
    fn write_msg(&mut self, frame: u32, msg: &ApiMsg);
    fn write_payload(&mut self, frame: u32, data: &[u8]);
}

pub fn set_recording_detour(detour: Option<Box<ApiRecordingReceiver>>) {
    let mut recorder = WEBRENDER_RECORDING_DETOUR.lock();
    *recorder.as_mut().unwrap().deref_mut() = detour;
}

fn write_data(frame: u32, data: &[u8]) {
    let filename = format!("record/frame_{}.bin", frame);
    let mut file = if !PathBuf::from(&filename).exists() {
        let mut file = File::create(filename).unwrap();

        let apimsg_type_id = unsafe {
            assert!(mem::size_of::<TypeId>() == mem::size_of::<u64>());
            mem::transmute::<TypeId, u64>(TypeId::of::<ApiMsg>())
        };
        file.write_u64::<LittleEndian>(apimsg_type_id).ok();
        file
    } else {
        OpenOptions::new().append(true).create(false).open(filename).unwrap()
    };
    file.write_u32::<LittleEndian>(data.len() as u32).ok();
    file.write(data).ok();
}

pub fn write_msg(frame: u32, msg: &ApiMsg) {
    match msg {
        &ApiMsg::AddRawFont(..) |
        &ApiMsg::AddNativeFont(..) |
        &ApiMsg::AddImage(..) |
        &ApiMsg::UpdateImage(..) |
        &ApiMsg::DeleteImage(..)|
        &ApiMsg::SetRootDisplayList(..) |
        &ApiMsg::SetRootPipeline(..) |
        &ApiMsg::Scroll(..) |
        &ApiMsg::TickScrollingBounce |
        &ApiMsg::WebGLCommand(..) => {
            let mut recorder = WEBRENDER_RECORDING_DETOUR.lock();
            if let Some(ref mut recorder) = recorder.as_mut().unwrap().as_mut() {
                recorder.write_msg(frame, &msg);
            } else {
                let buff = serialize(msg, bincode::SizeLimit::Infinite).unwrap();
                write_data(frame, &buff);
            }
       }
       _ => {}
    }
}

pub fn write_payload(frame: u32, data: &[u8]) {
    let mut recorder = WEBRENDER_RECORDING_DETOUR.lock();
    if let Some(ref mut recorder) = recorder.as_mut().unwrap().as_mut() {
        recorder.write_payload(frame, data);
    } else {
        write_data(frame, &[]); //signal the payload
        write_data(frame, data);
    }
}
<|endoftext|>