<commit_before>use crate::beacon_chain::BeaconChain;
use crate::error;
use crate::messages::NodeMessage;
use crate::service::{NetworkMessage, OutgoingMessage};
use crate::sync::SimpleSync;
use crossbeam_channel::{unbounded as channel, Sender};
use futures::future;
use futures::prelude::*;
use libp2p::{
    rpc::{RPCMethod, RPCRequest, RPCResponse},
    HelloMessage, PeerId, RPCEvent,
};
use slog::warn;
use slog::{debug, trace};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use types::Hash256;

/// Timeout for RPC requests.
const REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
/// Timeout before banning a peer for non-identification.
const HELLO_TIMEOUT: Duration = Duration::from_secs(30);

/// Handles messages received from the network and client and organises syncing.
pub struct MessageHandler {
    /// Currently loaded and initialised beacon chain.
    chain: Arc<BeaconChain>,
    /// The syncing framework.
    sync: SimpleSync,
    /// The network channel to relay messages to the Network service.
    network_send: crossbeam_channel::Sender<NetworkMessage>,
    /// A mapping of peers and the RPC id we have sent an RPC request to.
    requests: HashMap<(PeerId, u64), Instant>,
    /// A counter of request id for each peer.
    request_ids: HashMap<PeerId, u64>,
    /// The `MessageHandler` logger.
    log: slog::Logger,
}

/// Types of messages the handler can receive.
#[derive(Debug, Clone)]
pub enum HandlerMessage {
    /// We have initiated a connection to a new peer.
    PeerDialed(PeerId),
    /// Peer has disconnected,
    PeerDisconnected(PeerId),
    /// A Node message has been received.
    Message(PeerId, NodeMessage),
    /// An RPC response/request has been received.
    RPC(PeerId, RPCEvent),
}

impl MessageHandler {
    /// Initializes and runs the MessageHandler.
    pub fn new(
        beacon_chain: Arc<BeaconChain>,
        network_send: crossbeam_channel::Sender<NetworkMessage>,
        executor: &tokio::runtime::TaskExecutor,
        log: slog::Logger,
    ) -> error::Result<Sender<HandlerMessage>> {
        debug!(log, "Service starting");

        let (handler_send, handler_recv) = channel();

        // Initialise sync and begin processing in thread
        // generate the Message handler
        let sync = SimpleSync::new(beacon_chain.clone());

        let mut handler = MessageHandler {
            // TODO: The handler may not need a chain, perhaps only sync?
            chain: beacon_chain.clone(),
            sync,
            network_send,
            requests: HashMap::new(),
            request_ids: HashMap::new(),

            log: log.clone(),
        };

        // spawn handler task
        // TODO: Handle manual termination of thread
        executor.spawn(future::poll_fn(move || -> Result<_, _> {
            loop {
                handler.handle_message(handler_recv.recv().map_err(|_| {
                    debug!(log, "Network message handler terminated.");
                })?);
            }
        }));

        Ok(handler_send)
    }

    fn handle_message(&mut self, message: HandlerMessage) {
        match message {
            // we have initiated a connection to a peer
            HandlerMessage::PeerDialed(peer_id) => {
                let id = self.generate_request_id(&peer_id);
                self.send_hello(peer_id, id, true);
            }
            // we have received an RPC message request/response
            HandlerMessage::RPC(peer_id, rpc_event) => {
                self.handle_rpc_message(peer_id, rpc_event);
            }
            //TODO: Handle all messages
            _ => {}
        }
    }

    fn handle_rpc_message(&mut self, peer_id: PeerId, rpc_message: RPCEvent) {
        match rpc_message {
            RPCEvent::Request {
                id,
                method_id: _, // TODO: Clean up RPC Message types, have a cleaner type by this point.
                body,
            } => self.handle_rpc_request(peer_id, id, body),
            RPCEvent::Response {
                id,
                method_id: _,
                result,
            } => self.handle_rpc_response(peer_id, id, result),
        }
    }

    /// A new RPC request has been received from the network.
    fn handle_rpc_request(&mut self, peer_id: PeerId, id: u64, request: RPCRequest) {
        match request {
            RPCRequest::Hello(hello_message) => {
                self.handle_hello_request(peer_id, id, hello_message)
            }
        }
    }

    /// An RPC response has been received from the network.
    // we match on id and ignore responses past the timeout.
    fn handle_rpc_response(&mut self, peer_id: PeerId, id: u64, response: RPCResponse) {
        // if response id is related to a request, ignore (likely RPC timeout)
        if self.requests.remove(&(peer_id, id)).is_none() {
            return;
        }
    }

    fn handle_hello_request(&mut self, peer_id: PeerId, id: u64, hello_message: HelloMessage) {
        // send back a HELLO message
        self.send_hello(peer_id.clone(), id, false);
        // validate the peer
        if !self.sync.validate_peer(peer_id.clone(), hello_message) {
            debug!(
                self.log,
                "Peer dropped due to mismatching HELLO messages: {:?}", peer_id
            );
            //TODO: block/ban the peer
        }
    }

    fn handle_hello_response(&mut self, peer_id: PeerId, id: u64, response: HelloMessage) {
        debug!(self.log, "Hello response received from peer: {:?}", peer_id);
        // validate peer - decide whether to drop/ban or add to sync
        // TODO: Peer validation
    }

    /// Generates a new request id for a peer.
    fn generate_request_id(&mut self, peer_id: &PeerId) -> u64 {
        // generate a unique id for the peer
        let id = {
            let borrowed_id = self.request_ids.entry(peer_id.clone()).or_insert_with(|| 0);
            let id = borrowed_id.clone();
            //increment the counter
            *borrowed_id += 1;
            id
        };
        // register RPC request
        self.requests.insert((peer_id.clone(), id), Instant::now());
        debug!(
            self.log,
            "Hello request registered with peer: {:?}", peer_id
        );
        id
    }

    /// Sends a HELLO RPC request or response to a newly connected peer.
    //TODO: The boolean determines if sending request/respond, will be cleaner in the RPC re-write
    fn send_hello(&mut self, peer_id: PeerId, id: u64, request: bool) {
        let rpc_event = match request {
            true => RPCEvent::Request {
                id,
                method_id: RPCMethod::Hello.into(),
                body: RPCRequest::Hello(self.sync.generate_hello()),
            },
            false => RPCEvent::Response {
                id,
                method_id: RPCMethod::Hello.into(),
                result: RPCResponse::Hello(self.sync.generate_hello()),
            },
        };

        // send the hello request to the network
        trace!(self.log, "Sending HELLO message to peer {:?}", peer_id);
        self.send_rpc(peer_id, rpc_event);
    }

    /// Sends an RPC request/response to the network server.
    fn send_rpc(&self, peer_id: PeerId, rpc_event: RPCEvent) {
        self.network_send
            .send(NetworkMessage::Send(
                peer_id,
                OutgoingMessage::RPC(rpc_event),
            ))
            .unwrap_or_else(|_| {
                warn!(
                    self.log,
                    "Could not send RPC message to the network service"
                )
            });
    }
}
<commit_msg>Tidy message handler<commit_after>use crate::beacon_chain::BeaconChain;
use crate::error;
use crate::messages::NodeMessage;
use crate::service::{NetworkMessage, OutgoingMessage};
use crate::sync::SimpleSync;
use crossbeam_channel::{unbounded as channel, Sender};
use futures::future;
use futures::prelude::*;
use libp2p::{
    rpc::{RPCMethod, RPCRequest, RPCResponse},
    HelloMessage, PeerId, RPCEvent,
};
use slog::warn;
use slog::{debug, trace};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use types::Hash256;

/// Timeout for RPC requests.
const REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
/// Timeout before banning a peer for non-identification.
const HELLO_TIMEOUT: Duration = Duration::from_secs(30);

/// Handles messages received from the network and client and organises syncing.
pub struct MessageHandler {
    /// Currently loaded and initialised beacon chain.
    chain: Arc<BeaconChain>,
    /// The syncing framework.
    sync: SimpleSync,
    /// The network channel to relay messages to the Network service.
    network_send: crossbeam_channel::Sender<NetworkMessage>,
    /// A mapping of peers and the RPC id we have sent an RPC request to.
    requests: HashMap<(PeerId, u64), Instant>,
    /// A counter of request id for each peer.
    request_ids: HashMap<PeerId, u64>,
    /// The `MessageHandler` logger.
    log: slog::Logger,
}

/// Types of messages the handler can receive.
#[derive(Debug, Clone)]
pub enum HandlerMessage {
    /// We have initiated a connection to a new peer.
    PeerDialed(PeerId),
    /// Peer has disconnected,
    PeerDisconnected(PeerId),
    /// A Node message has been received.
    Message(PeerId, NodeMessage),
    /// An RPC response/request has been received.
    RPC(PeerId, RPCEvent),
}

impl MessageHandler {
    /// Initializes and runs the MessageHandler.
    pub fn new(
        beacon_chain: Arc<BeaconChain>,
        network_send: crossbeam_channel::Sender<NetworkMessage>,
        executor: &tokio::runtime::TaskExecutor,
        log: slog::Logger,
    ) -> error::Result<Sender<HandlerMessage>> {
        debug!(log, "Service starting");

        let (handler_send, handler_recv) = channel();

        // Initialise sync and begin processing in thread
        // generate the Message handler
        let sync = SimpleSync::new(beacon_chain.clone());

        let mut handler = MessageHandler {
            // TODO: The handler may not need a chain, perhaps only sync?
            chain: beacon_chain.clone(),
            sync,
            network_send,
            requests: HashMap::new(),
            request_ids: HashMap::new(),

            log: log.clone(),
        };

        // spawn handler task
        // TODO: Handle manual termination of thread
        executor.spawn(future::poll_fn(move || -> Result<_, _> {
            loop {
                handler.handle_message(handler_recv.recv().map_err(|_| {
                    debug!(log, "Network message handler terminated.");
                })?);
            }
        }));

        Ok(handler_send)
    }

    /// Handle all messages incoming from the network service.
    fn handle_message(&mut self, message: HandlerMessage) {
        match message {
            // we have initiated a connection to a peer
            HandlerMessage::PeerDialed(peer_id) => {
                let id = self.generate_request_id(&peer_id);
                self.send_hello(peer_id, id, true);
            }
            // we have received an RPC message request/response
            HandlerMessage::RPC(peer_id, rpc_event) => {
                self.handle_rpc_message(peer_id, rpc_event);
            }
            //TODO: Handle all messages
            _ => {}
        }
    }

    /* RPC - Related functionality */

    /// Handle RPC messages
    fn handle_rpc_message(&mut self, peer_id: PeerId, rpc_message: RPCEvent) {
        match rpc_message {
            RPCEvent::Request {
                id,
                method_id: _, // TODO: Clean up RPC Message types, have a cleaner type by this point.
                body,
            } => self.handle_rpc_request(peer_id, id, body),
            RPCEvent::Response {
                id,
                method_id: _,
                result,
            } => self.handle_rpc_response(peer_id, id, result),
        }
    }

    /// A new RPC request has been received from the network.
    fn handle_rpc_request(&mut self, peer_id: PeerId, id: u64, request: RPCRequest) {
        match request {
            RPCRequest::Hello(hello_message) => {
                self.handle_hello_request(peer_id, id, hello_message)
            }
        }
    }

    /// An RPC response has been received from the network.
    // we match on id and ignore responses past the timeout.
    fn handle_rpc_response(&mut self, peer_id: PeerId, id: u64, response: RPCResponse) {
        // if response id is related to a request, ignore (likely RPC timeout)
        if self.requests.remove(&(peer_id, id)).is_none() {
            return;
        }
    }

    /// Handle a HELLO RPC request message.
    fn handle_hello_request(&mut self, peer_id: PeerId, id: u64, hello_message: HelloMessage) {
        // send back a HELLO message
        self.send_hello(peer_id.clone(), id, false);
        // validate the peer
        if !self.sync.validate_peer(peer_id.clone(), hello_message) {
            debug!(
                self.log,
                "Peer dropped due to mismatching HELLO messages: {:?}", peer_id
            );
            //TODO: block/ban the peer
        }
    }

    /// Handle a HELLO RPC response message.
    fn handle_hello_response(&mut self, peer_id: PeerId, id: u64, response: HelloMessage) {
        debug!(self.log, "Hello response received from peer: {:?}", peer_id);
        // validate peer - decide whether to drop/ban or add to sync
        // TODO: Peer validation
    }

    /* General RPC helper functions */

    /// Generates a new request id for a peer.
    fn generate_request_id(&mut self, peer_id: &PeerId) -> u64 {
        // generate a unique id for the peer
        let id = {
            let borrowed_id = self.request_ids.entry(peer_id.clone()).or_insert_with(|| 0);
            let id = borrowed_id.clone();
            //increment the counter
            *borrowed_id += 1;
            id
        };
        // register RPC request
        self.requests.insert((peer_id.clone(), id), Instant::now());
        debug!(
            self.log,
            "Hello request registered with peer: {:?}", peer_id
        );
        id
    }

    /// Sends a HELLO RPC request or response to a newly connected peer.
    //TODO: The boolean determines if sending request/respond, will be cleaner in the RPC re-write
    fn send_hello(&mut self, peer_id: PeerId, id: u64, request: bool) {
        let rpc_event = match request {
            true => RPCEvent::Request {
                id,
                method_id: RPCMethod::Hello.into(),
                body: RPCRequest::Hello(self.sync.generate_hello()),
            },
            false => RPCEvent::Response {
                id,
                method_id: RPCMethod::Hello.into(),
                result: RPCResponse::Hello(self.sync.generate_hello()),
            },
        };

        // send the hello request to the network
        trace!(self.log, "Sending HELLO message to peer {:?}", peer_id);
        self.send_rpc(peer_id, rpc_event);
    }

    /// Sends an RPC request/response to the network server.
    fn send_rpc(&self, peer_id: PeerId, rpc_event: RPCEvent) {
        self.network_send
            .send(NetworkMessage::Send(
                peer_id,
                OutgoingMessage::RPC(rpc_event),
            ))
            .unwrap_or_else(|_| {
                warn!(
                    self.log,
                    "Could not send RPC message to the network service"
                )
            });
    }
}
<|endoftext|>


<commit_before>use criterion::Criterion;
use lucet_runtime_internals::context::{Context, ContextHandle};

/// Time the initialization of a context.
fn context_init(c: &mut Criterion) {
    extern "C" fn f() {}

    let mut stack = vec![0u64; 1024].into_boxed_slice();

    c.bench_function("context_init", move |b| {
        b.iter(|| {
            let mut parent = ContextHandle::new();
            ContextHandle::create_and_init(
                &mut *stack,
                &mut parent,
                f as *const extern "C" fn(),
                &[],
            )
            .unwrap();
        })
    });
}

/// Time the swap from an already-initialized context to a guest function and back.
fn context_swap_return(c: &mut Criterion) {
    extern "C" fn f() {}

    c.bench_function("context_swap_return", move |b| {
        b.iter_batched(
            || {
                let mut stack = vec![0u64; 1024].into_boxed_slice();
                let mut parent = ContextHandle::new();
                let child = ContextHandle::create_and_init(
                    &mut *stack,
                    &mut parent,
                    f as *const extern "C" fn(),
                    &[],
                )
                .unwrap();
                (stack, parent, child)
            },
            |(stack, mut parent, child)| unsafe {
                Context::swap(&mut parent, &child);
                stack
            },
            criterion::BatchSize::PerIteration,
        )
    });
}

/// Time the call to sigprocmask as used in `Context::init()`.
fn context_sigprocmask(c: &mut Criterion) {
    use nix::sys::signal;
    c.bench_function("context_sigprocmask", |b| {
        b.iter_batched(
            || signal::SigSet::empty(),
            |mut sigset| {
                signal::sigprocmask(signal::SigmaskHow::SIG_SETMASK, None, Some(&mut sigset))
                    .unwrap()
            },
            criterion::BatchSize::PerIteration,
        )
    });
}

pub fn context_benches(c: &mut Criterion) {
    context_init(c);
    context_swap_return(c);
    context_sigprocmask(c);
}
<commit_msg>[lucet-benchmarks] add many-arg context benchmark<commit_after>use criterion::Criterion;
use lucet_runtime_internals::context::{Context, ContextHandle};

/// Time the initialization of a context.
fn context_init(c: &mut Criterion) {
    extern "C" fn f() {}

    let mut stack = vec![0u64; 1024].into_boxed_slice();

    c.bench_function("context_init", move |b| {
        b.iter(|| {
            let mut parent = ContextHandle::new();
            ContextHandle::create_and_init(
                &mut *stack,
                &mut parent,
                f as *const extern "C" fn(),
                &[],
            )
            .unwrap();
        })
    });
}

/// Time the swap from an already-initialized context to a guest function and back.
fn context_swap_return(c: &mut Criterion) {
    extern "C" fn f() {}

    c.bench_function("context_swap_return", move |b| {
        b.iter_batched(
            || {
                let mut stack = vec![0u64; 1024].into_boxed_slice();
                let mut parent = ContextHandle::new();
                let child = ContextHandle::create_and_init(
                    &mut *stack,
                    &mut parent,
                    f as *const extern "C" fn(),
                    &[],
                )
                .unwrap();
                (stack, parent, child)
            },
            |(stack, mut parent, child)| unsafe {
                Context::swap(&mut parent, &child);
                stack
            },
            criterion::BatchSize::PerIteration,
        )
    });
}

/// Swap to a trivial guest function that takes a bunch of arguments.
fn context_swap_return_many_args(c: &mut Criterion) {
    extern "C" fn f(
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
        _: u8,
        _: u16,
        _: u32,
        _: u64,
        _: f32,
        _: f64,
    ) {
    }

    let args = vec![
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
        0xAFu8.into(),
        0xAFu16.into(),
        0xAFu32.into(),
        0xAFu64.into(),
        175.0f32.into(),
        175.0f64.into(),
    ];

    c.bench_function("context_swap_return_many_args", move |b| {
        b.iter_batched(
            || {
                let mut stack = vec![0u64; 1024].into_boxed_slice();
                let mut parent = ContextHandle::new();
                let child = ContextHandle::create_and_init(
                    &mut *stack,
                    &mut parent,
                    f as *const extern "C" fn(),
                    &args,
                )
                .unwrap();
                (stack, parent, child)
            },
            |(stack, mut parent, child)| unsafe {
                Context::swap(&mut parent, &child);
                stack
            },
            criterion::BatchSize::PerIteration,
        )
    });
}

/// Time the call to sigprocmask as used in `Context::init()`.
fn context_sigprocmask(c: &mut Criterion) {
    use nix::sys::signal;
    c.bench_function("context_sigprocmask", |b| {
        b.iter_batched(
            || signal::SigSet::empty(),
            |mut sigset| {
                signal::sigprocmask(signal::SigmaskHow::SIG_SETMASK, None, Some(&mut sigset))
                    .unwrap()
            },
            criterion::BatchSize::PerIteration,
        )
    });
}

pub fn context_benches(c: &mut Criterion) {
    context_init(c);
    context_swap_return(c);
    context_swap_return_many_args(c);
    context_sigprocmask(c);
}
<|endoftext|>


<commit_before>#![feature(libc,question_mark)]
extern crate libc;

use std::any::Any;
use std::fs;
use std::io;
use std::io::Read;
use std::path::Path;
use std::process::{Command, Stdio};
use std::str::from_utf8_unchecked;

/// Shortcut to path->filename conversion.
///
/// Returns the unchanged `path` if there is a character encoding error or something.
pub fn filename<'a> (path: &'a str) -> &'a str {
  match Path::new (path) .file_name() {
    Some (filename) => match filename.to_str() {
      Some (fstr) => if fstr.ends_with (".rs") {&fstr[0 .. fstr.len() - 3]} else {fstr},
      None => path},
    None => path}}

mod gstuff {pub fn filename<'a> (path: &'a str) -> &'a str {super::filename (path)}}

// A trick to use the `try_s` macro from both the outside and inside of the crate.
//mod gstuff {pub fn filename<'a> (path: &'a str) -> &'a str {::filename (path)}}

/// Returns on error, converting the `Err` value to `String` and prepending the current location.
///
/// cf. http://www.reddit.com/r/rust/comments/29wwzw/error_handling_and_result_types/cipcm9a
#[macro_export] macro_rules! try_s {
  ($e: expr) => {match $e {Ok (ok) => ok, Err (err) => {return Err (format! ("{}:{}] {}", ::gstuff::filename (file!()), line!(), err));}}}}

// Like `try_s`, but takes a reference.
#[macro_export] macro_rules! try_sp {
  ($e: expr) => {match $e {&Ok (ref ok) => ok,
    &Err (ref err) => {return Err (format! ("{}:{}] {:?}", ::gstuff::filename (file!()), line!(), err));}}}}

/// Returns a `Err(String)`, prepending the current location (file name and line number) to the string.
///
/// Examples: `ERR! ("too bad")`; `ERR! ("{}", foo)`;
#[macro_export] macro_rules! ERR {
  ($format: expr, $($args: tt)+) => {Err (format! (concat! ("{}:{}] ", $format), ::gstuff::filename (file!()), line!(), $($args)+))};
  ($format: expr) => {Err (format! (concat! ("{}:{}] ", $format), ::gstuff::filename (file!()), line!()))}}

/// A helper to build a string on the stack.
///
/// Given an array it makes a writeable cursor available to the passed code block.
///
/// Returns a &str slice pointing at memory between the array start and the cursor.
///
/// Example:
///
///     use std::io::Write;
///     let mut foobar: [u8; 128] = unsafe {std::mem::uninitialized()};
///     let foobar = gstring! (foobar, {
///         write! (foobar, "foo") .expect ("!write");
///         write! (foobar, "bar") .expect ("!write");
///     });
///
/// Alternatives: https://crates.io/crates/stack.
#[macro_export] macro_rules! gstring {($array: ident, $code: block) => {{
  let end = {
    let mut $array = ::std::io::Cursor::new (&mut $array[..]);
    let $array = &mut $array;
    $code;
    $array.position() as usize};
  unsafe {::std::str::from_utf8_unchecked (&$array[0..end])}}}}

/// Takes a netstring from the front of the slice.
///
/// Returns the unpacked netstring and the remainder of the slice.
pub fn netstring (at: &[u8]) -> Result<(&[u8], &[u8]), String> {
  let length_end = match at.iter().position (|&ch| ch < b'0' || ch > b'9') {Some (l) if l > 0 => l, _ => return ERR! ("No len.")};
  match at.get (length_end) {Some (&ch) if ch == b':' => (), _ => return ERR! ("No colon.")};
  let length = unsafe {from_utf8_unchecked (&at[0 .. length_end])};
  let length: usize = try_s! (length.parse());
  let bulk_pos = 0 + length_end + 1;
  let bulk_end = bulk_pos + length;
  match at.get (bulk_end) {Some (&ch) if ch == b',' => (), _ => return ERR! ("No comma.")}
  Ok ((&at[bulk_pos .. bulk_end], &at[bulk_end + 1 ..]))}

/// Wraps `gethostname` to fetch the current hostname into a temporary buffer.
#[cfg(unix)]
pub fn with_hostname (visitor: &mut FnMut (&[u8])) -> Result<(), std::io::Error> {
  use libc::{size_t, gethostname};  // http://man7.org/linux/man-pages/man2/gethostname.2.html
  use std::ffi::CStr;
  use std::io;

  let mut buf = [0; 128];
  let rc = unsafe {gethostname (buf.as_mut_ptr(), (buf.len() - 1) as size_t)};
  if rc == 0 {
    let cs = unsafe {CStr::from_ptr (buf.as_ptr())};
    Ok (visitor (cs.to_bytes()))
  } else {
    Err (io::Error::last_os_error())}}

#[cfg(unix)] #[test] fn test_hostname() {
  let mut hostname = String::new();
  with_hostname (&mut |bytes| hostname = String::from_utf8_lossy (bytes) .into_owned()) .unwrap();}

/// Read contents of the file into a String.
///
/// Returns an empty string if the file is not present under the given path.
pub fn slurp (path: &AsRef<Path>) -> String {
  let mut file = match fs::File::open (path) {
    Ok (f) => f,
    Err (ref err) if err.kind() == io::ErrorKind::NotFound => return String::new(),
    Err (err) => panic! ("Can't open {:?}: {}", path.as_ref(), err)};
  let mut buf = String::new();
  file.read_to_string (&mut buf) .expect ("!read");
  buf}

/// Runs a command in a shell, returning stderr+stdout on success.
///
/// Sometimes we need something simpler than constructing a Command.
///
/// If the command failed then returns it's stderr
pub fn slurp_prog (command: &str) -> Result<String, String> {
  let output = match Command::new ("bash") .arg ("-c") .arg (command) .output() {
    Ok (output) => output,
    Err (ref err) if err.kind() == io::ErrorKind::NotFound => {  // "sh" was not found, try a different name.
      try_s! (Command::new ("sh") .arg ("-c") .arg (command) .output())},
    Err (err) => return ERR! ("{}", err)};

  let combined_output: String = if output.stderr.is_empty() {
    try_s! (String::from_utf8 (output.stdout))
  } else if output.stdout.is_empty() {
    try_s! (String::from_utf8 (output.stderr))
  } else {
    let mut buf = String::with_capacity (output.stderr.len() + output.stdout.len());
    buf.push_str (try_s! (std::str::from_utf8 (&output.stderr[..])));
    buf.push_str (try_s! (std::str::from_utf8 (&output.stdout[..])));
    buf};

  if output.status.success() {Ok (combined_output)} else {Err (combined_output)}}

#[test] fn test_slurp_prog() {
  let foo = match slurp_prog ("echo foo") {Ok (foo) => foo, Err (err) => panic! ("{}", err)};
  assert_eq! (foo.trim(), "foo");}

/// Run a command, printing it first. Stdout and stderr are forwarded through (`inherit`).
pub fn cmd (cmd: &str) -> Result<(), String> {
  println! ("$ {}", cmd);
  let status = try_s! (Command::new ("bash") .arg ("-c") .arg (cmd) .stdout (Stdio::inherit()) .stderr (Stdio::inherit()) .status());
  if !status.success() {Err (format! ("Command returned an error status: {}", status))} else {Ok(())}}

/// Useful with panic handlers.
pub fn any_to_str<'a> (message: &'a Box<Any + Send + 'static>) -> Option<&'a str> {
  if let Some (message) = message.downcast_ref::<&str>() {return Some (message)}
  if let Some (message) = message.downcast_ref::<String>() {return Some (&message[..])}
  return None}

// --- nom extensions ---

/// Implements the /(?x) (.*?) (remainder)/ pattern:
/// looks for remainder first, then returns a tuple with the prefix and the remainder.
/// Discussion: https://www.reddit.com/r/rust/comments/4yokxd/crash_course_into_nom_kind_of_question/
#[macro_export]
macro_rules! take_until_parse_s (
  ($i: expr, $remainder: ident! ($($args:tt)*)) => ({
    let input = $i as &str;
    let mut ret = IResult::Error (nom::Err::Position (nom::ErrorKind::Custom (0), input));
    for (pos, _) in $i.char_indices() {
      match $remainder! (&input[pos..], $($args)*) {
        IResult::Done (i,o)    => {ret = IResult::Done (i, (&input[0..pos], o)); break},  // Found the remainder!
        IResult::Error(_)      => continue,  // Keep looking.
        IResult::Incomplete(_) => continue}}  // Keep looking.

    if !ret.is_done() {
      // Last chance. See if subparser accepts an empty string.
      if let IResult::Done (i,o) = $remainder! ("", $($args)*) {
        ret = IResult::Done (i, (input, o))}}  // Empty remainder was accepted.

    ret});

  ($i: expr, $f: expr) => (take_until_parse_s! ($i, call! ($f));););

/// `$starts` is an optional `Pattern` used to optimize the `$remainder` search.
/// For example, with jetscii: `take_until_find_parse_s! ("foo bar", ascii_chars! ('b'), tag_s! ("bar"))`.
#[macro_export]
macro_rules! take_until_find_parse_s (
  ($i: expr, $starts: expr, $remainder: ident! ($($args:tt)*)) => ({
    let input = $i as &str;
    let mut ret = IResult::Error (nom::Err::Position (nom::ErrorKind::Custom (0), input));
    for (pos, _) in $i.match_indices ($starts) {
      match $remainder! (&input[pos..], $($args)*) {
        IResult::Done (i,o)    => {ret = IResult::Done (i, (&input[0..pos], o)); break},  // Found the remainder!
        IResult::Error(_)      => continue,  // Keep looking.
        IResult::Incomplete(_) => continue}}  // Keep looking.
    ret});

  ($i: expr, $starts: expr, $f: expr) => (take_until_find_parse_s! ($i, $starts, call! ($f));););
<commit_msg>Nom-based match replacement: parse_replace_s.<commit_after>#![feature(libc,question_mark)]
extern crate libc;

use std::any::Any;
use std::fs;
use std::io;
use std::io::Read;
use std::path::Path;
use std::process::{Command, Stdio};
use std::str::from_utf8_unchecked;

/// Shortcut to path->filename conversion.
///
/// Returns the unchanged `path` if there is a character encoding error or something.
pub fn filename<'a> (path: &'a str) -> &'a str {
  match Path::new (path) .file_name() {
    Some (filename) => match filename.to_str() {
      Some (fstr) => if fstr.ends_with (".rs") {&fstr[0 .. fstr.len() - 3]} else {fstr},
      None => path},
    None => path}}

mod gstuff {pub fn filename<'a> (path: &'a str) -> &'a str {super::filename (path)}}

// A trick to use the `try_s` macro from both the outside and inside of the crate.
//mod gstuff {pub fn filename<'a> (path: &'a str) -> &'a str {::filename (path)}}

/// Returns on error, converting the `Err` value to `String` and prepending the current location.
///
/// cf. http://www.reddit.com/r/rust/comments/29wwzw/error_handling_and_result_types/cipcm9a
#[macro_export] macro_rules! try_s {
  ($e: expr) => {match $e {Ok (ok) => ok, Err (err) => {return Err (format! ("{}:{}] {}", ::gstuff::filename (file!()), line!(), err));}}}}

// Like `try_s`, but takes a reference.
#[macro_export] macro_rules! try_sp {
  ($e: expr) => {match $e {&Ok (ref ok) => ok,
    &Err (ref err) => {return Err (format! ("{}:{}] {:?}", ::gstuff::filename (file!()), line!(), err));}}}}

/// Returns a `Err(String)`, prepending the current location (file name and line number) to the string.
///
/// Examples: `ERR! ("too bad")`; `ERR! ("{}", foo)`;
#[macro_export] macro_rules! ERR {
  ($format: expr, $($args: tt)+) => {Err (format! (concat! ("{}:{}] ", $format), ::gstuff::filename (file!()), line!(), $($args)+))};
  ($format: expr) => {Err (format! (concat! ("{}:{}] ", $format), ::gstuff::filename (file!()), line!()))}}

/// A helper to build a string on the stack.
///
/// Given an array it makes a writeable cursor available to the passed code block.
///
/// Returns a &str slice pointing at memory between the array start and the cursor.
///
/// Example:
///
///     use std::io::Write;
///     let mut foobar: [u8; 128] = unsafe {std::mem::uninitialized()};
///     let foobar = gstring! (foobar, {
///         write! (foobar, "foo") .expect ("!write");
///         write! (foobar, "bar") .expect ("!write");
///     });
///
/// Alternatives: https://crates.io/crates/stack.
#[macro_export] macro_rules! gstring {($array: ident, $code: block) => {{
  let end = {
    let mut $array = ::std::io::Cursor::new (&mut $array[..]);
    let $array = &mut $array;
    $code;
    $array.position() as usize};
  unsafe {::std::str::from_utf8_unchecked (&$array[0..end])}}}}

/// Takes a netstring from the front of the slice.
///
/// Returns the unpacked netstring and the remainder of the slice.
pub fn netstring (at: &[u8]) -> Result<(&[u8], &[u8]), String> {
  let length_end = match at.iter().position (|&ch| ch < b'0' || ch > b'9') {Some (l) if l > 0 => l, _ => return ERR! ("No len.")};
  match at.get (length_end) {Some (&ch) if ch == b':' => (), _ => return ERR! ("No colon.")};
  let length = unsafe {from_utf8_unchecked (&at[0 .. length_end])};
  let length: usize = try_s! (length.parse());
  let bulk_pos = 0 + length_end + 1;
  let bulk_end = bulk_pos + length;
  match at.get (bulk_end) {Some (&ch) if ch == b',' => (), _ => return ERR! ("No comma.")}
  Ok ((&at[bulk_pos .. bulk_end], &at[bulk_end + 1 ..]))}

/// Wraps `gethostname` to fetch the current hostname into a temporary buffer.
#[cfg(unix)]
pub fn with_hostname (visitor: &mut FnMut (&[u8])) -> Result<(), std::io::Error> {
  use libc::{size_t, gethostname};  // http://man7.org/linux/man-pages/man2/gethostname.2.html
  use std::ffi::CStr;
  use std::io;

  let mut buf = [0; 128];
  let rc = unsafe {gethostname (buf.as_mut_ptr(), (buf.len() - 1) as size_t)};
  if rc == 0 {
    let cs = unsafe {CStr::from_ptr (buf.as_ptr())};
    Ok (visitor (cs.to_bytes()))
  } else {
    Err (io::Error::last_os_error())}}

#[cfg(unix)] #[test] fn test_hostname() {
  let mut hostname = String::new();
  with_hostname (&mut |bytes| hostname = String::from_utf8_lossy (bytes) .into_owned()) .unwrap();}

/// Read contents of the file into a String.
///
/// Returns an empty string if the file is not present under the given path.
pub fn slurp (path: &AsRef<Path>) -> String {
  let mut file = match fs::File::open (path) {
    Ok (f) => f,
    Err (ref err) if err.kind() == io::ErrorKind::NotFound => return String::new(),
    Err (err) => panic! ("Can't open {:?}: {}", path.as_ref(), err)};
  let mut buf = String::new();
  file.read_to_string (&mut buf) .expect ("!read");
  buf}

/// Runs a command in a shell, returning stderr+stdout on success.
///
/// Sometimes we need something simpler than constructing a Command.
///
/// If the command failed then returns it's stderr
pub fn slurp_prog (command: &str) -> Result<String, String> {
  let output = match Command::new ("bash") .arg ("-c") .arg (command) .output() {
    Ok (output) => output,
    Err (ref err) if err.kind() == io::ErrorKind::NotFound => {  // "sh" was not found, try a different name.
      try_s! (Command::new ("sh") .arg ("-c") .arg (command) .output())},
    Err (err) => return ERR! ("{}", err)};

  let combined_output: String = if output.stderr.is_empty() {
    try_s! (String::from_utf8 (output.stdout))
  } else if output.stdout.is_empty() {
    try_s! (String::from_utf8 (output.stderr))
  } else {
    let mut buf = String::with_capacity (output.stderr.len() + output.stdout.len());
    buf.push_str (try_s! (std::str::from_utf8 (&output.stderr[..])));
    buf.push_str (try_s! (std::str::from_utf8 (&output.stdout[..])));
    buf};

  if output.status.success() {Ok (combined_output)} else {Err (combined_output)}}

#[test] fn test_slurp_prog() {
  let foo = match slurp_prog ("echo foo") {Ok (foo) => foo, Err (err) => panic! ("{}", err)};
  assert_eq! (foo.trim(), "foo");}

/// Run a command, printing it first. Stdout and stderr are forwarded through (`inherit`).
pub fn cmd (cmd: &str) -> Result<(), String> {
  println! ("$ {}", cmd);
  let status = try_s! (Command::new ("bash") .arg ("-c") .arg (cmd) .stdout (Stdio::inherit()) .stderr (Stdio::inherit()) .status());
  if !status.success() {Err (format! ("Command returned an error status: {}", status))} else {Ok(())}}

/// Useful with panic handlers.
pub fn any_to_str<'a> (message: &'a Box<Any + Send + 'static>) -> Option<&'a str> {
  if let Some (message) = message.downcast_ref::<&str>() {return Some (message)}
  if let Some (message) = message.downcast_ref::<String>() {return Some (&message[..])}
  return None}

// --- nom extensions ---

/// Implements the `/(?x) (.*?) (remainder)/` pattern:
/// looks for remainder first, then returns a tuple with the prefix and the remainder.
///
/// Discussion: https://www.reddit.com/r/rust/comments/4yokxd/crash_course_into_nom_kind_of_question/
#[macro_export]
macro_rules! take_until_parse_s (
  ($i: expr, $remainder: ident! ($($args:tt)*)) => ({
    let input = $i as &str;
    let mut ret = IResult::Error (nom::Err::Position (nom::ErrorKind::Custom (0), input));
    for (pos, _) in $i.char_indices() {
      match $remainder! (&input[pos..], $($args)*) {
        IResult::Done (i,o)    => {ret = IResult::Done (i, (&input[0..pos], o)); break},  // Found the remainder!
        IResult::Error(_)      => continue,  // Keep looking.
        IResult::Incomplete(_) => continue}}  // Keep looking.

    if !ret.is_done() {
      // Last chance. See if subparser accepts an empty string.
      if let IResult::Done (i,o) = $remainder! ("", $($args)*) {
        ret = IResult::Done (i, (input, o))}}  // Empty remainder was accepted.

    ret});

  ($i: expr, $f: expr) => (take_until_parse_s! ($i, call! ($f));););

/// Uses nom to find all matches of the given `$submac` parser and replace them with the `$submac` results.
///
/// For exaple, replacing "foo" with "bar" might look like this:
///
///     let bar_bar = parse_replace_s! ("foo bar", map! (tag_s! ("foo"), |_| "bar"));
#[macro_export]
macro_rules! parse_replace_s {
  ($i: expr, $submac: ident! ($($args:tt)*)) => ({
    let input = $i as &str;
    let mut output = String::with_capacity (input.len() * 2 + 32);
    let mut pos = input;
    while let IResult::Done (tail, (head, evaluation)) = take_until_parse_s! (pos, $submac! ($($args)*)) {
      output.push_str (head);
      output.push_str (&evaluation);
      pos = tail;}
    output.push_str (pos);
    output});

  ($i: expr, $f: expr) => (parse_replace! ($i, call! ($f)););}

/// `$starts` is an optional `Pattern` used to optimize the `$remainder` search.
///
/// For example, with jetscii: `take_until_find_parse_s! ("foo bar", ascii_chars! ('b'), tag_s! ("bar"))`.
#[macro_export]
macro_rules! take_until_find_parse_s (
  ($i: expr, $starts: expr, $remainder: ident! ($($args:tt)*)) => ({
    let input = $i as &str;
    let mut ret = IResult::Error (nom::Err::Position (nom::ErrorKind::Custom (0), input));
    for (pos, _) in $i.match_indices ($starts) {
      match $remainder! (&input[pos..], $($args)*) {
        IResult::Done (i,o)    => {ret = IResult::Done (i, (&input[0..pos], o)); break},  // Found the remainder!
        IResult::Error(_)      => continue,  // Keep looking.
        IResult::Incomplete(_) => continue}}  // Keep looking.
    ret});

  ($i: expr, $starts: expr, $f: expr) => (take_until_find_parse_s! ($i, $starts, call! ($f));););

/// Uses nom to find all matches of the given `$submac` parser and replace them with the parser's evaluations.
///
/// `$starts` is a `Pattern` that is used to speed up the search. It must match the beginning of any `$submac`-compatible substrings.
///
/// For exaple, replacing "foo" with "bar" might look like this:
///
///     let bar_bar = find_parse_replace_s! ("foo bar", ascii_chars! ('f'), map! (tag_s! ("foo"), |_| "bar"));
#[macro_export]
macro_rules! find_parse_replace_s {
  ($i: expr, $starts: expr, $submac: ident! ($($args:tt)*)) => ({
    let input = $i as &str;
    let mut output = String::with_capacity (input.len() * 2 + 32);
    let mut pos = input;
    while let IResult::Done (tail, (head, evaluation)) =
      take_until_find_parse_s! (pos, $starts, $submac! ($($args)*)) {
        output.push_str (head);
        output.push_str (&evaluation);
        pos = tail;}
    output.push_str (pos);
    output});

  ($i: expr, $starts: expr, $f: expr) => (find_parse_replace! ($i, $starts, call! ($f)););}
<|endoftext|>


<commit_before>// Copyright 2015 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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 platform::{self, OsIpcChannel, OsIpcReceiver, OsIpcSender, OsIpcOneShotServer};
use platform::{OsUnknownIpcChannel};

use serde::json;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::cell::RefCell;
use std::marker::PhantomData;
use std::mem;

thread_local! {
    static OS_IPC_CHANNELS_FOR_DESERIALIZATION: RefCell<Vec<OsUnknownIpcChannel>> =
        RefCell::new(Vec::new())
}
thread_local! {
    static OS_IPC_CHANNELS_FOR_SERIALIZATION: RefCell<Vec<OsIpcChannel>> = RefCell::new(Vec::new())
}

pub fn channel<T>() -> Result<(IpcSender<T>, IpcReceiver<T>),()> where T: Deserialize + Serialize {
    let (os_sender, os_receiver) = match platform::channel() {
        Ok((os_sender, os_receiver)) => (os_sender, os_receiver),
        Err(_) => return Err(()),
    };
    let ipc_receiver = IpcReceiver {
        os_receiver: os_receiver,
        phantom: PhantomData,
    };
    let ipc_sender = IpcSender {
        os_sender: os_sender,
        phantom: PhantomData,
    };
    Ok((ipc_sender, ipc_receiver))
}

pub struct IpcReceiver<T> where T: Deserialize + Serialize {
    os_receiver: OsIpcReceiver,
    phantom: PhantomData<T>,
}

impl<T> IpcReceiver<T> where T: Deserialize + Serialize {
    pub fn recv(&self) -> Result<T,()> {
        match self.os_receiver.recv() {
            Ok((data, os_ipc_senders)) => deserialize_received_data(&data[..], os_ipc_senders),
            Err(_) => Err(()),
        }
    }
}

impl<T> Deserialize for IpcReceiver<T> where T: Deserialize + Serialize {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: Deserializer {
        let index: usize = try!(Deserialize::deserialize(deserializer));
        let os_receiver =
            OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| {
                // FIXME(pcwalton): This could panic. Return some sort of nice error.
                os_ipc_channels_for_deserialization.borrow_mut()[index].to_receiver()
            });
        Ok(IpcReceiver {
            os_receiver: os_receiver,
            phantom: PhantomData,
        })
    }
}

impl<T> Serialize for IpcReceiver<T> where T: Deserialize + Serialize {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(),S::Error> where S: Serializer {
        let index = OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| {
            let mut os_ipc_channels_for_serialization =
                os_ipc_channels_for_serialization.borrow_mut();
            let index = os_ipc_channels_for_serialization.len();
            os_ipc_channels_for_serialization.push(OsIpcChannel::Receiver(self.os_receiver
                                                                              .consume()));
            index
        });
        index.serialize(serializer)
    }
}

#[derive(Clone)]
pub struct IpcSender<T> where T: Serialize {
    os_sender: OsIpcSender,
    phantom: PhantomData<T>,
}

impl<T> IpcSender<T> where T: Serialize {
    pub fn connect(name: String) -> Result<IpcSender<T>,()> {
        let os_sender = match OsIpcSender::connect(name) {
            Ok(os_sender) => os_sender,
            Err(_) => return Err(()),
        };
        Ok(IpcSender {
            os_sender: os_sender,
            phantom: PhantomData,
        })
    }

    pub fn send(&self, data: T) -> Result<(),()> {
        let mut bytes = Vec::with_capacity(4096);
        OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| {
            let old_os_ipc_channels =
                mem::replace(&mut *os_ipc_channels_for_serialization.borrow_mut(), Vec::new());
            let os_ipc_channels = {
                let mut serializer = json::Serializer::new(&mut bytes);
                data.serialize(&mut serializer).unwrap();
                mem::replace(&mut *os_ipc_channels_for_serialization.borrow_mut(),
                             old_os_ipc_channels)
            };
            self.os_sender.send(&bytes[..], os_ipc_channels).map_err(|_| ())
        })
    }
}

impl<T> Deserialize for IpcSender<T> where T: Serialize {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: Deserializer {
        let index: usize = try!(Deserialize::deserialize(deserializer));
        let os_sender =
            OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| {
                // FIXME(pcwalton): This could panic. Return some sort of nice error.
                os_ipc_channels_for_deserialization.borrow_mut()[index].to_sender()
            });
        Ok(IpcSender {
            os_sender: os_sender,
            phantom: PhantomData,
        })
    }
}

impl<T> Serialize for IpcSender<T> where T: Serialize {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(),S::Error> where S: Serializer {
        let index = OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| {
            let mut os_ipc_channels_for_serialization =
                os_ipc_channels_for_serialization.borrow_mut();
            let index = os_ipc_channels_for_serialization.len();
            os_ipc_channels_for_serialization.push(OsIpcChannel::Sender(self.os_sender.clone()));
            index
        });
        index.serialize(serializer)
    }
}

pub struct IpcOneShotServer<T> {
    os_server: OsIpcOneShotServer,
    phantom: PhantomData<T>,
}

impl<T> IpcOneShotServer<T> where T: Deserialize + Serialize {
    pub fn new() -> Result<(IpcOneShotServer<T>, String),()> {
        let (os_server, name) = match OsIpcOneShotServer::new() {
            Ok(result) => result,
            Err(_) => return Err(()),
        };
        Ok((IpcOneShotServer {
            os_server: os_server,
            phantom: PhantomData,
        }, name))
    }

    pub fn accept(self) -> Result<(IpcReceiver<T>,T),()> {
        let (os_receiver, data, os_channels) = match self.os_server.accept() {
            Ok(result) => result,
            Err(_) => return Err(()),
        };
        let value = try!(deserialize_received_data(&data[..], os_channels));
        Ok((IpcReceiver {
            os_receiver: os_receiver,
            phantom: PhantomData,
        }, value))
    }
}

fn deserialize_received_data<T>(data: &[u8], mut os_ipc_channels: Vec<OsUnknownIpcChannel>)
                                -> Result<T,()>
                                where T: Deserialize + Serialize {
    OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| {
        mem::swap(&mut *os_ipc_channels_for_deserialization.borrow_mut(), &mut os_ipc_channels);
        let mut deserializer = match json::Deserializer::new(data.iter()
                                                                 .map(|byte| Ok(*byte))) {
            Ok(deserializer) => deserializer,
            Err(_) => return Err(()),
        };
        let result = match Deserialize::deserialize(&mut deserializer) {
            Ok(result) => result,
            Err(_) => return Err(()),
        };
        mem::swap(&mut *os_ipc_channels_for_deserialization.borrow_mut(), &mut os_ipc_channels);
        Ok(result)
    })
}

<commit_msg>Make IPC senders cloneable regardless of whether their contents are<commit_after>// Copyright 2015 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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 platform::{self, OsIpcChannel, OsIpcReceiver, OsIpcSender, OsIpcOneShotServer};
use platform::{OsUnknownIpcChannel};

use serde::json;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::cell::RefCell;
use std::marker::PhantomData;
use std::mem;

thread_local! {
    static OS_IPC_CHANNELS_FOR_DESERIALIZATION: RefCell<Vec<OsUnknownIpcChannel>> =
        RefCell::new(Vec::new())
}
thread_local! {
    static OS_IPC_CHANNELS_FOR_SERIALIZATION: RefCell<Vec<OsIpcChannel>> = RefCell::new(Vec::new())
}

pub fn channel<T>() -> Result<(IpcSender<T>, IpcReceiver<T>),()> where T: Deserialize + Serialize {
    let (os_sender, os_receiver) = match platform::channel() {
        Ok((os_sender, os_receiver)) => (os_sender, os_receiver),
        Err(_) => return Err(()),
    };
    let ipc_receiver = IpcReceiver {
        os_receiver: os_receiver,
        phantom: PhantomData,
    };
    let ipc_sender = IpcSender {
        os_sender: os_sender,
        phantom: PhantomData,
    };
    Ok((ipc_sender, ipc_receiver))
}

pub struct IpcReceiver<T> where T: Deserialize + Serialize {
    os_receiver: OsIpcReceiver,
    phantom: PhantomData<T>,
}

impl<T> IpcReceiver<T> where T: Deserialize + Serialize {
    pub fn recv(&self) -> Result<T,()> {
        match self.os_receiver.recv() {
            Ok((data, os_ipc_senders)) => deserialize_received_data(&data[..], os_ipc_senders),
            Err(_) => Err(()),
        }
    }
}

impl<T> Deserialize for IpcReceiver<T> where T: Deserialize + Serialize {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: Deserializer {
        let index: usize = try!(Deserialize::deserialize(deserializer));
        let os_receiver =
            OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| {
                // FIXME(pcwalton): This could panic. Return some sort of nice error.
                os_ipc_channels_for_deserialization.borrow_mut()[index].to_receiver()
            });
        Ok(IpcReceiver {
            os_receiver: os_receiver,
            phantom: PhantomData,
        })
    }
}

impl<T> Serialize for IpcReceiver<T> where T: Deserialize + Serialize {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(),S::Error> where S: Serializer {
        let index = OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| {
            let mut os_ipc_channels_for_serialization =
                os_ipc_channels_for_serialization.borrow_mut();
            let index = os_ipc_channels_for_serialization.len();
            os_ipc_channels_for_serialization.push(OsIpcChannel::Receiver(self.os_receiver
                                                                              .consume()));
            index
        });
        index.serialize(serializer)
    }
}

pub struct IpcSender<T> where T: Serialize {
    os_sender: OsIpcSender,
    phantom: PhantomData<T>,
}

impl<T> Clone for IpcSender<T> where T: Serialize {
    fn clone(&self) -> IpcSender<T> {
        IpcSender {
            os_sender: self.os_sender.clone(),
            phantom: PhantomData,
        }
    }
}

impl<T> IpcSender<T> where T: Serialize {
    pub fn connect(name: String) -> Result<IpcSender<T>,()> {
        let os_sender = match OsIpcSender::connect(name) {
            Ok(os_sender) => os_sender,
            Err(_) => return Err(()),
        };
        Ok(IpcSender {
            os_sender: os_sender,
            phantom: PhantomData,
        })
    }

    pub fn send(&self, data: T) -> Result<(),()> {
        let mut bytes = Vec::with_capacity(4096);
        OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| {
            let old_os_ipc_channels =
                mem::replace(&mut *os_ipc_channels_for_serialization.borrow_mut(), Vec::new());
            let os_ipc_channels = {
                let mut serializer = json::Serializer::new(&mut bytes);
                data.serialize(&mut serializer).unwrap();
                mem::replace(&mut *os_ipc_channels_for_serialization.borrow_mut(),
                             old_os_ipc_channels)
            };
            self.os_sender.send(&bytes[..], os_ipc_channels).map_err(|_| ())
        })
    }
}

impl<T> Deserialize for IpcSender<T> where T: Serialize {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: Deserializer {
        let index: usize = try!(Deserialize::deserialize(deserializer));
        let os_sender =
            OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| {
                // FIXME(pcwalton): This could panic. Return some sort of nice error.
                os_ipc_channels_for_deserialization.borrow_mut()[index].to_sender()
            });
        Ok(IpcSender {
            os_sender: os_sender,
            phantom: PhantomData,
        })
    }
}

impl<T> Serialize for IpcSender<T> where T: Serialize {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(),S::Error> where S: Serializer {
        let index = OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| {
            let mut os_ipc_channels_for_serialization =
                os_ipc_channels_for_serialization.borrow_mut();
            let index = os_ipc_channels_for_serialization.len();
            os_ipc_channels_for_serialization.push(OsIpcChannel::Sender(self.os_sender.clone()));
            index
        });
        index.serialize(serializer)
    }
}

pub struct IpcOneShotServer<T> {
    os_server: OsIpcOneShotServer,
    phantom: PhantomData<T>,
}

impl<T> IpcOneShotServer<T> where T: Deserialize + Serialize {
    pub fn new() -> Result<(IpcOneShotServer<T>, String),()> {
        let (os_server, name) = match OsIpcOneShotServer::new() {
            Ok(result) => result,
            Err(_) => return Err(()),
        };
        Ok((IpcOneShotServer {
            os_server: os_server,
            phantom: PhantomData,
        }, name))
    }

    pub fn accept(self) -> Result<(IpcReceiver<T>,T),()> {
        let (os_receiver, data, os_channels) = match self.os_server.accept() {
            Ok(result) => result,
            Err(_) => return Err(()),
        };
        let value = try!(deserialize_received_data(&data[..], os_channels));
        Ok((IpcReceiver {
            os_receiver: os_receiver,
            phantom: PhantomData,
        }, value))
    }
}

fn deserialize_received_data<T>(data: &[u8], mut os_ipc_channels: Vec<OsUnknownIpcChannel>)
                                -> Result<T,()>
                                where T: Deserialize + Serialize {
    OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| {
        mem::swap(&mut *os_ipc_channels_for_deserialization.borrow_mut(), &mut os_ipc_channels);
        let mut deserializer = match json::Deserializer::new(data.iter()
                                                                 .map(|byte| Ok(*byte))) {
            Ok(deserializer) => deserializer,
            Err(_) => return Err(()),
        };
        let result = match Deserialize::deserialize(&mut deserializer) {
            Ok(result) => result,
            Err(_) => return Err(()),
        };
        mem::swap(&mut *os_ipc_channels_for_deserialization.borrow_mut(), &mut os_ipc_channels);
        Ok(result)
    })
}

<|endoftext|>


<commit_before>use jomini::{
    ArrayReader, Encoding, ObjectReader, Operator, Scalar, TextTape, TextToken, Utf8Encoding,
    ValueReader, Windows1252Encoding,
};
use js_sys::{Array, Date};
use ser::SerTape;
use std::fmt::Write;
use wasm_bindgen::prelude::*;
mod op;
mod ser;

/// wee_alloc saved ~6kb in the wasm payload and there was no
/// measurable performance difference
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

/// Custom bindings to avoid using fallible `Reflect` for plain objects.
/// Lifted from serde-wasm-bindgen where it brought a nice performance boost:
/// https://github.com/cloudflare/serde-wasm-bindgen/commit/f763ead4b47d6cda5873d18f276be4fa6712f6a6
#[wasm_bindgen]
extern "C" {
    type Object;

    #[wasm_bindgen(constructor)]
    fn new() -> Object;

    #[wasm_bindgen(method, indexing_setter)]
    fn set(this: &Object, key: JsValue, value: JsValue);
}

fn create_error_msg(err: &dyn std::error::Error) -> String {
    let mut msg = String::new();
    let _ = write!(msg, "{}", err);

    let mut ie = err.source();
    while let Some(cause) = ie {
        let _ = write!(msg, ". Caused by: {}", cause);
        ie = cause.source();
    }

    msg
}

fn data_to_js_date(data: &[u8]) -> Option<Date> {
    enum DateState {
        Empty,
        Year,
        Month,
        Day,
    }

    let mut state = DateState::Empty;
    let mut y = 0;
    let mut m = 0;
    let mut d = 0;
    let mut start = 0;
    let mut pos = 0;

    for &c in data {
        if c == b'.' {
            let span = Scalar::new(&data[start..pos]);
            if let Ok(x) = span.to_i64() {
                match state {
                    DateState::Empty => {
                        y = x as i32;
                        state = DateState::Year;
                    }
                    DateState::Year => {
                        m = x as i32 - 1;
                        state = DateState::Month;
                    }
                    DateState::Month => {
                        d = x as i32;
                        state = DateState::Day;
                    }
                    _ => {
                        return None;
                    }
                }
                start = pos + 1;
            } else {
                return None;
            }
        } else if c > b'9' || (c < b'0' && c != b'-') {
            return None;
        }

        pos += 1;
    }

    let span = Scalar::new(&data[start..pos]);
    span.to_u64().ok().and_then(|x| match state {
        DateState::Month => {
            let res = Date::new(&JsValue::from_f64(Date::utc(y as f64, m as f64)));
            res.set_utc_date(x as u32);
            Some(res)
        }
        DateState::Day => {
            let res = Date::new(&JsValue::from_f64(Date::utc(y as f64, m as f64)));
            res.set_utc_date(d as u32);
            res.set_utc_hours(x as u32);
            Some(res)
        }
        _ => None,
    })
}

// We skip the header for jomini
fn skip_header(data: &[u8]) -> &[u8] {
    for (pos, &c) in data.iter().enumerate() {
        if c == b'=' {
            return data;
        } else if c.is_ascii_control() {
            return &data[pos..];
        }
    }

    data
}

#[derive(Debug)]
struct InObjectifier<'a, 'b, E> {
    reader: ObjectReader<'a, 'b, E>,
}

impl<'a, 'b, E: Encoding> InObjectifier<'a, 'b, E>
where
    E: Clone,
{
    fn new(tape: &'b TextTape<'a>, encoding: E) -> Self {
        let reader = ObjectReader::new(tape, encoding);

        Self { reader }
    }

    fn from_root(&self) -> JsValue {
        self.create_object(self.reader.clone()).into()
    }

    fn scalar_to_js_value(&self, reader: ValueReader<'a, 'b, E>) -> JsValue {
        let scalar = reader.read_scalar().unwrap();
        if let Ok(x) = scalar.to_bool() {
            return JsValue::from_bool(x);
        }

        if let Ok(x) = scalar.to_f64() {
            return JsValue::from_f64(x);
        }

        if let Some(x) = data_to_js_date(scalar.view_data()) {
            return x.into();
        }

        JsValue::from_str(reader.read_str().unwrap().as_ref())
    }

    fn create_array(&self, mut reader: ArrayReader<'a, 'b, E>) -> JsValue {
        let len = reader.values_len();
        if len == 0 {
            // Surprise! You thought we'd be creating an array. The only reason
            // we aren't for empty arrays is that it is ambiguous whether it is
            // an empty array or object. To keep with previous jomini behavior,
            // we maintain that empty arrays / objects are written as objects.
            return Object::new().into();
        }

        let arr = Array::new_with_length(len as u32);
        let mut pos = 0;
        while let Some(x) = reader.next_value() {
            let v = self.entry_to_js(None, x);
            arr.set(pos, v);
            pos += 1;
        }

        arr.into()
    }

    fn create_from_header(&self, mut veader: ArrayReader<'a, 'b, E>) -> Object {
        let result = Object::new();

        let key = JsValue::from_str(veader.next_value().unwrap().read_str().unwrap().as_ref());
        let val = self.entry_to_js(None, veader.next_value().unwrap());

        result.set(key, val);
        result
    }

    fn create_operator_object(&self, op: Operator, veader: ValueReader<'a, 'b, E>) -> Object {
        let result = Object::new();
        let val = self.entry_to_js(None, veader);
        result.set(JsValue::from_str(op::operator_name(op)), val);
        result
    }

    fn entry_to_js(&self, op: Option<Operator>, veader: ValueReader<'a, 'b, E>) -> JsValue {
        if let Some(op) = op {
            self.create_operator_object(op, veader).into()
        } else {
            match veader.token() {
                TextToken::Quoted(_) | TextToken::Unquoted(_) => self.scalar_to_js_value(veader),
                TextToken::Array(_) => self.create_array(veader.read_array().unwrap()),
                TextToken::Object(_) | TextToken::HiddenObject(_) => {
                    self.create_object(veader.read_object().unwrap()).into()
                }
                TextToken::Header(_) => {
                    self.create_from_header(veader.read_array().unwrap()).into()
                }
                TextToken::End(_) | TextToken::Operator(_) => JsValue::null(),
            }
        }
    }

    fn create_object(&self, mut reader: ObjectReader<'a, 'b, E>) -> Object {
        let result = Object::new();
        while let Some((key, mut entries)) = reader.next_fields() {
            let key_js = JsValue::from_str(key.read_str().as_ref());

            let value_js = if entries.len() == 1 {
                let (op, value) = entries.pop().unwrap();
                self.entry_to_js(op, value)
            } else {
                let arr = Array::new_with_length(entries.len() as u32);
                for (i, (op, value)) in entries.drain(..).enumerate() {
                    let v = self.entry_to_js(op, value);
                    arr.set(i as u32, v);
                }
                arr.into()
            };

            result.set(key_js, value_js);
        }

        result
    }

    fn find_query_in<'c>(
        &mut self,
        needle: &str,
        mut rest: impl Iterator<Item = &'c str>,
        reader: &mut ObjectReader<'a, 'b, E>,
    ) -> JsValue {
        let mut values: Vec<JsValue> = Vec::new();
        let nested_need = rest.next();
        while let Some((key, op, value)) = reader.next_field() {
            if key.read_str() != needle {
                continue;
            }

            if let Some(nested_needle) = nested_need {
                let mut nreader = value.read_object().unwrap();
                return self.find_query_in(nested_needle, rest, &mut nreader);
            }

            let lvalue = self.entry_to_js(op, value);
            values.push(lvalue);
        }

        if values.is_empty() {
            JsValue::undefined()
        } else if values.len() == 1 {
            values.pop().unwrap()
        } else {
            let arr = Array::new_with_length(values.len() as u32);
            for (i, val) in values.drain(..).enumerate() {
                arr.set(i as u32, val);
            }
            arr.into()
        }
    }

    fn find_query(&mut self, query: &str) -> JsValue {
        let mut q = query.split('/').skip(1);
        let root = if let Some(x) = q.next() {
            x
        } else {
            return JsValue::undefined();
        };

        let mut cl = self.reader.clone();
        self.find_query_in(root, q, &mut cl)
    }
}

/// Holds the parsed data for a short period of time, allowing a user to extract all or a subset of the
/// parsed document
#[wasm_bindgen]
pub struct Query {
    // We need this field so that our referenced data isn't reclaimed
    _backing_data: Vec<u8>,
    tape: TextTape<'static>,
    encoding: JsValue,
}

#[wasm_bindgen]
impl Query {
    /// Convert the entire document into an object
    pub fn root(&self) -> Result<JsValue, JsValue> {
        match self.encoding.as_string().as_deref() {
            Some("windows1252") => {
                let io = InObjectifier::new(&self.tape, Windows1252Encoding::new());
                Ok(io.from_root().into())
            }
            _ => {
                let io = InObjectifier::new(&self.tape, Utf8Encoding::new());
                Ok(io.from_root().into())
            }
        }
    }

    /// Convert the entire document into a JSON string
    pub fn json(&self) -> Result<JsValue, JsValue> {
        match self.encoding.as_string().as_deref() {
            Some("windows1252") => {
                let reader = SerTape::new(&self.tape, Windows1252Encoding::new());
                let result = serde_json::to_string(&reader).unwrap();
                let val = JsValue::from_str(&result);
                Ok(val)
            }
            _ => {
                let reader = SerTape::new(&self.tape, Utf8Encoding::new());
                let result = serde_json::to_string(&reader).unwrap();
                let val = JsValue::from_str(&result);
                Ok(val)
            }
        }
    }

    /// Return the object, array, or value pointed at by the query. Uses a format similar to JSON pointer:
    ///
    /// ```ignore
    /// /player
    /// /countries/ENG/prestige
    /// ```
    pub fn at(&self, query: &str) -> Result<JsValue, JsValue> {
        match self.encoding.as_string().as_deref() {
            Some("windows1252") => {
                let mut io = InObjectifier::new(&self.tape, Windows1252Encoding::new());
                Ok(io.find_query(query))
            }
            _ => {
                let mut io = InObjectifier::new(&self.tape, Utf8Encoding::new());
                Ok(io.find_query(query))
            }
        }
    }
}

#[wasm_bindgen]
pub fn parse_text(d: Vec<u8>, encoding: JsValue) -> Result<Query, JsValue> {
    let data = skip_header(d.as_slice());

    let tape =
        TextTape::from_slice(data).map_err(|e| JsValue::from_str(create_error_msg(&e).as_str()))?;

    // Cast away the lifetime so that we can store it in a wasm-bindgen compatible struct
    let tape: TextTape<'static> = unsafe { std::mem::transmute(tape) };

    Ok(Query {
        tape,
        encoding,
        _backing_data: d,
    })
}
<commit_msg>Remove useless conversion<commit_after>use jomini::{
    ArrayReader, Encoding, ObjectReader, Operator, Scalar, TextTape, TextToken, Utf8Encoding,
    ValueReader, Windows1252Encoding,
};
use js_sys::{Array, Date};
use ser::SerTape;
use std::fmt::Write;
use wasm_bindgen::prelude::*;
mod op;
mod ser;

/// wee_alloc saved ~6kb in the wasm payload and there was no
/// measurable performance difference
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

/// Custom bindings to avoid using fallible `Reflect` for plain objects.
/// Lifted from serde-wasm-bindgen where it brought a nice performance boost:
/// https://github.com/cloudflare/serde-wasm-bindgen/commit/f763ead4b47d6cda5873d18f276be4fa6712f6a6
#[wasm_bindgen]
extern "C" {
    type Object;

    #[wasm_bindgen(constructor)]
    fn new() -> Object;

    #[wasm_bindgen(method, indexing_setter)]
    fn set(this: &Object, key: JsValue, value: JsValue);
}

fn create_error_msg(err: &dyn std::error::Error) -> String {
    let mut msg = String::new();
    let _ = write!(msg, "{}", err);

    let mut ie = err.source();
    while let Some(cause) = ie {
        let _ = write!(msg, ". Caused by: {}", cause);
        ie = cause.source();
    }

    msg
}

fn data_to_js_date(data: &[u8]) -> Option<Date> {
    enum DateState {
        Empty,
        Year,
        Month,
        Day,
    }

    let mut state = DateState::Empty;
    let mut y = 0;
    let mut m = 0;
    let mut d = 0;
    let mut start = 0;
    let mut pos = 0;

    for &c in data {
        if c == b'.' {
            let span = Scalar::new(&data[start..pos]);
            if let Ok(x) = span.to_i64() {
                match state {
                    DateState::Empty => {
                        y = x as i32;
                        state = DateState::Year;
                    }
                    DateState::Year => {
                        m = x as i32 - 1;
                        state = DateState::Month;
                    }
                    DateState::Month => {
                        d = x as i32;
                        state = DateState::Day;
                    }
                    _ => {
                        return None;
                    }
                }
                start = pos + 1;
            } else {
                return None;
            }
        } else if c > b'9' || (c < b'0' && c != b'-') {
            return None;
        }

        pos += 1;
    }

    let span = Scalar::new(&data[start..pos]);
    span.to_u64().ok().and_then(|x| match state {
        DateState::Month => {
            let res = Date::new(&JsValue::from_f64(Date::utc(y as f64, m as f64)));
            res.set_utc_date(x as u32);
            Some(res)
        }
        DateState::Day => {
            let res = Date::new(&JsValue::from_f64(Date::utc(y as f64, m as f64)));
            res.set_utc_date(d as u32);
            res.set_utc_hours(x as u32);
            Some(res)
        }
        _ => None,
    })
}

// We skip the header for jomini
fn skip_header(data: &[u8]) -> &[u8] {
    for (pos, &c) in data.iter().enumerate() {
        if c == b'=' {
            return data;
        } else if c.is_ascii_control() {
            return &data[pos..];
        }
    }

    data
}

#[derive(Debug)]
struct InObjectifier<'a, 'b, E> {
    reader: ObjectReader<'a, 'b, E>,
}

impl<'a, 'b, E: Encoding> InObjectifier<'a, 'b, E>
where
    E: Clone,
{
    fn new(tape: &'b TextTape<'a>, encoding: E) -> Self {
        let reader = ObjectReader::new(tape, encoding);

        Self { reader }
    }

    fn from_root(&self) -> JsValue {
        self.create_object(self.reader.clone()).into()
    }

    fn scalar_to_js_value(&self, reader: ValueReader<'a, 'b, E>) -> JsValue {
        let scalar = reader.read_scalar().unwrap();
        if let Ok(x) = scalar.to_bool() {
            return JsValue::from_bool(x);
        }

        if let Ok(x) = scalar.to_f64() {
            return JsValue::from_f64(x);
        }

        if let Some(x) = data_to_js_date(scalar.view_data()) {
            return x.into();
        }

        JsValue::from_str(reader.read_str().unwrap().as_ref())
    }

    fn create_array(&self, mut reader: ArrayReader<'a, 'b, E>) -> JsValue {
        let len = reader.values_len();
        if len == 0 {
            // Surprise! You thought we'd be creating an array. The only reason
            // we aren't for empty arrays is that it is ambiguous whether it is
            // an empty array or object. To keep with previous jomini behavior,
            // we maintain that empty arrays / objects are written as objects.
            return Object::new().into();
        }

        let arr = Array::new_with_length(len as u32);
        let mut pos = 0;
        while let Some(x) = reader.next_value() {
            let v = self.entry_to_js(None, x);
            arr.set(pos, v);
            pos += 1;
        }

        arr.into()
    }

    fn create_from_header(&self, mut veader: ArrayReader<'a, 'b, E>) -> Object {
        let result = Object::new();

        let key = JsValue::from_str(veader.next_value().unwrap().read_str().unwrap().as_ref());
        let val = self.entry_to_js(None, veader.next_value().unwrap());

        result.set(key, val);
        result
    }

    fn create_operator_object(&self, op: Operator, veader: ValueReader<'a, 'b, E>) -> Object {
        let result = Object::new();
        let val = self.entry_to_js(None, veader);
        result.set(JsValue::from_str(op::operator_name(op)), val);
        result
    }

    fn entry_to_js(&self, op: Option<Operator>, veader: ValueReader<'a, 'b, E>) -> JsValue {
        if let Some(op) = op {
            self.create_operator_object(op, veader).into()
        } else {
            match veader.token() {
                TextToken::Quoted(_) | TextToken::Unquoted(_) => self.scalar_to_js_value(veader),
                TextToken::Array(_) => self.create_array(veader.read_array().unwrap()),
                TextToken::Object(_) | TextToken::HiddenObject(_) => {
                    self.create_object(veader.read_object().unwrap()).into()
                }
                TextToken::Header(_) => {
                    self.create_from_header(veader.read_array().unwrap()).into()
                }
                TextToken::End(_) | TextToken::Operator(_) => JsValue::null(),
            }
        }
    }

    fn create_object(&self, mut reader: ObjectReader<'a, 'b, E>) -> Object {
        let result = Object::new();
        while let Some((key, mut entries)) = reader.next_fields() {
            let key_js = JsValue::from_str(key.read_str().as_ref());

            let value_js = if entries.len() == 1 {
                let (op, value) = entries.pop().unwrap();
                self.entry_to_js(op, value)
            } else {
                let arr = Array::new_with_length(entries.len() as u32);
                for (i, (op, value)) in entries.drain(..).enumerate() {
                    let v = self.entry_to_js(op, value);
                    arr.set(i as u32, v);
                }
                arr.into()
            };

            result.set(key_js, value_js);
        }

        result
    }

    fn find_query_in<'c>(
        &mut self,
        needle: &str,
        mut rest: impl Iterator<Item = &'c str>,
        reader: &mut ObjectReader<'a, 'b, E>,
    ) -> JsValue {
        let mut values: Vec<JsValue> = Vec::new();
        let nested_need = rest.next();
        while let Some((key, op, value)) = reader.next_field() {
            if key.read_str() != needle {
                continue;
            }

            if let Some(nested_needle) = nested_need {
                let mut nreader = value.read_object().unwrap();
                return self.find_query_in(nested_needle, rest, &mut nreader);
            }

            let lvalue = self.entry_to_js(op, value);
            values.push(lvalue);
        }

        if values.is_empty() {
            JsValue::undefined()
        } else if values.len() == 1 {
            values.pop().unwrap()
        } else {
            let arr = Array::new_with_length(values.len() as u32);
            for (i, val) in values.drain(..).enumerate() {
                arr.set(i as u32, val);
            }
            arr.into()
        }
    }

    fn find_query(&mut self, query: &str) -> JsValue {
        let mut q = query.split('/').skip(1);
        let root = if let Some(x) = q.next() {
            x
        } else {
            return JsValue::undefined();
        };

        let mut cl = self.reader.clone();
        self.find_query_in(root, q, &mut cl)
    }
}

/// Holds the parsed data for a short period of time, allowing a user to extract all or a subset of the
/// parsed document
#[wasm_bindgen]
pub struct Query {
    // We need this field so that our referenced data isn't reclaimed
    _backing_data: Vec<u8>,
    tape: TextTape<'static>,
    encoding: JsValue,
}

#[wasm_bindgen]
impl Query {
    /// Convert the entire document into an object
    pub fn root(&self) -> Result<JsValue, JsValue> {
        match self.encoding.as_string().as_deref() {
            Some("windows1252") => {
                let io = InObjectifier::new(&self.tape, Windows1252Encoding::new());
                Ok(io.from_root())
            }
            _ => {
                let io = InObjectifier::new(&self.tape, Utf8Encoding::new());
                Ok(io.from_root())
            }
        }
    }

    /// Convert the entire document into a JSON string
    pub fn json(&self) -> Result<JsValue, JsValue> {
        match self.encoding.as_string().as_deref() {
            Some("windows1252") => {
                let reader = SerTape::new(&self.tape, Windows1252Encoding::new());
                let result = serde_json::to_string(&reader).unwrap();
                let val = JsValue::from_str(&result);
                Ok(val)
            }
            _ => {
                let reader = SerTape::new(&self.tape, Utf8Encoding::new());
                let result = serde_json::to_string(&reader).unwrap();
                let val = JsValue::from_str(&result);
                Ok(val)
            }
        }
    }

    /// Return the object, array, or value pointed at by the query. Uses a format similar to JSON pointer:
    ///
    /// ```ignore
    /// /player
    /// /countries/ENG/prestige
    /// ```
    pub fn at(&self, query: &str) -> Result<JsValue, JsValue> {
        match self.encoding.as_string().as_deref() {
            Some("windows1252") => {
                let mut io = InObjectifier::new(&self.tape, Windows1252Encoding::new());
                Ok(io.find_query(query))
            }
            _ => {
                let mut io = InObjectifier::new(&self.tape, Utf8Encoding::new());
                Ok(io.find_query(query))
            }
        }
    }
}

#[wasm_bindgen]
pub fn parse_text(d: Vec<u8>, encoding: JsValue) -> Result<Query, JsValue> {
    let data = skip_header(d.as_slice());

    let tape =
        TextTape::from_slice(data).map_err(|e| JsValue::from_str(create_error_msg(&e).as_str()))?;

    // Cast away the lifetime so that we can store it in a wasm-bindgen compatible struct
    let tape: TextTape<'static> = unsafe { std::mem::transmute(tape) };

    Ok(Query {
        tape,
        encoding,
        _backing_data: d,
    })
}
<|endoftext|>


<commit_before>extern crate rsrl;

use rand::{rngs::StdRng, SeedableRng};
use rsrl::{
    control::td::SARSALambda,
    domains::{Domain, MountainCar},
    fa::linear::{
        basis::{Combinators, Fourier},
        optim::SGD,
        LFA,
    },
    make_shared,
    params::Parameterised,
    policies::{EpsilonGreedy, Greedy, Policy, Random},
    spaces::Space,
    traces::Trace,
    Handler,
};

const ALPHA: f64 = 0.01;
const GAMMA: f64 = 0.99;
const LAMBDA: f64 = 0.7;

fn main() {
    let env = MountainCar::default();

    let mut rng = StdRng::seed_from_u64(0);
    let mut agent = {
        let n_actions = env.action_space().card().into();

        let basis = Fourier::from_space(5, env.state_space()).with_bias();
        let q_func = make_shared(LFA::vector(basis, SGD(1.0), n_actions));

        let policy = EpsilonGreedy::new(Greedy::new(q_func.clone()), Random::new(n_actions), 0.2);
        let wdim = q_func.weights_dim();

        let trace = Trace::replacing(wdim, GAMMA, LAMBDA);

        SARSALambda::new(q_func, policy, trace, ALPHA, GAMMA)
    };

    for e in 0..1000 {
        // Episode loop:
        let mut j = 0;
        let mut env = MountainCar::default();
        let mut action = agent.policy.sample(&mut rng, env.emit().state());

        for i in 0.. {
            // Trajectory loop:
            j = i;

            let t = env.transition(action);

            agent.handle(&t).ok();
            action = agent.policy.sample(&mut rng, t.to.state());

            if t.terminated() {
                break;
            }
        }

        agent.policy.epsilon *= 0.995;

        println!("Batch {}: {} steps...", e + 1, j + 1);
    }

    let traj = MountainCar::default().rollout(|s| agent.policy.mode(s), Some(1000));

    println!("OOS: {} states...", traj.n_states());
}
<commit_msg>fix(examples): update SARSALambda example script<commit_after>extern crate rsrl;

use rand::{rngs::StdRng, SeedableRng};
use rsrl::{
    control::td::SARSALambda,
    domains::{Domain, MountainCar},
    fa::linear::{
        basis::{Combinators, Fourier},
        optim::SGD,
        LFA,
    },
    make_shared,
    params::Parameterised,
    policies::{EpsilonGreedy, Greedy, Policy, Random},
    spaces::Space,
    traces::Trace,
    Handler,
};

const ALPHA: f64 = 0.01;
const GAMMA: f64 = 0.99;
const LAMBDA: f64 = 0.7;

fn main() {
    let env = MountainCar::default();

    let mut rng = StdRng::seed_from_u64(0);
    let mut agent = {
        let n_actions = env.action_space().card().into();

        let basis = Fourier::from_space(5, env.state_space()).with_bias();
        let fa_theta = make_shared(LFA::vector(basis, SGD(1.0), n_actions));

        let policy = EpsilonGreedy::new(Greedy::new(fa_theta.clone()), Random::new(n_actions), 0.2);
        let wdim = fa_theta.weights_dim();

        let trace = Trace::replacing(wdim, GAMMA, LAMBDA);

        SARSALambda {
            fa_theta,
            policy,
            trace,
            alpha: ALPHA,
            gamma: GAMMA,
        }
    };

    for e in 0..1000 {
        // Episode loop:
        let mut j = 0;
        let mut env = MountainCar::default();
        let mut action = agent.policy.sample(&mut rng, env.emit().state());

        for i in 0.. {
            // Trajectory loop:
            j = i;

            let t = env.transition(action);

            agent.handle(&t).ok();
            action = agent.policy.sample(&mut rng, t.to.state());

            if t.terminated() {
                break;
            }
        }

        agent.policy.epsilon *= 0.995;

        println!("Batch {}: {} steps...", e + 1, j + 1);
    }

    let traj = MountainCar::default().rollout(|s| agent.policy.mode(s), Some(1000));

    println!("OOS: {} states...", traj.n_states());
}
<|endoftext|>


<commit_before>
<commit_msg>nucleotide-count: Added template to stub file<commit_after>use std::collections::HashMap;

pub fn count(nucleotide: char, dna: &str) -> Result<usize, ()> {
    unimplemented!(
        "How much of nucleotide type '{}' is contained inside DNA string '{}'?",
        nucleotide,
        dna
    );
}

pub fn nucleotide_counts(dna: &str) -> Result<HashMap<char, usize>, char> {
    unimplemented!(
        "How much of every nucleotide type is contained inside DNA string '{}'?",
        dna
    );
}
<|endoftext|>