<commit_before>use crate::rpc::methods::BlockRootSlot;
use crate::rpc::{RPCEvent, RPCMessage, Rpc};
use crate::NetworkConfig;
use futures::prelude::*;
use libp2p::{
    core::{
        swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess},
        PublicKey,
    },
    gossipsub::{Gossipsub, GossipsubEvent},
    identify::{protocol::IdentifyInfo, Identify, IdentifyEvent},
    ping::{Ping, PingEvent},
    tokio_io::{AsyncRead, AsyncWrite},
    NetworkBehaviour, PeerId,
};
use slog::{debug, o, warn};
use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz_derive::{Decode, Encode};
use types::Attestation;
use types::{Topic, TopicHash};

/// Builds the network behaviour for the libp2p Swarm.
/// Implements gossipsub message routing.
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")]
pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite> {
    /// The routing pub-sub mechanism for eth2.
    gossipsub: Gossipsub<TSubstream>,
    // TODO: Add Kademlia for peer discovery
    /// The events generated by this behaviour to be consumed in the swarm poll.
    serenity_rpc: Rpc<TSubstream>,
    /// Allows discovery of IP addresses for peers on the network.
    identify: Identify<TSubstream>,
    /// Keep regular connection to peers and disconnect if absent.
    // TODO: Keepalive, likely remove this later.
    // TODO: Make the ping time customizeable.
    ping: Ping<TSubstream>,
    #[behaviour(ignore)]
    events: Vec<BehaviourEvent>,
    /// Logger for behaviour actions.
    #[behaviour(ignore)]
    log: slog::Logger,
}

// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: GossipsubEvent) {
        match event {
            GossipsubEvent::Message(gs_msg) => {
                let pubsub_message = match PubsubMessage::ssz_decode(&gs_msg.data, 0) {
                    //TODO: Punish peer on error
                    Err(e) => {
                        warn!(
                            self.log,
                            "Received undecodable message from Peer {:?}", gs_msg.source
                        );
                        return;
                    }
                    Ok((msg, _index)) => msg,
                };

                self.events.push(BehaviourEvent::GossipMessage {
                    source: gs_msg.source,
                    topics: gs_msg.topics,
                    message: pubsub_message,
                });
            }
            GossipsubEvent::Subscribed {
                peer_id: _,
                topic: _,
            }
            | GossipsubEvent::Unsubscribed {
                peer_id: _,
                topic: _,
            } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<RPCMessage>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: RPCMessage) {
        match event {
            RPCMessage::PeerDialed(peer_id) => {
                self.events.push(BehaviourEvent::PeerDialed(peer_id))
            }
            RPCMessage::RPC(peer_id, rpc_event) => {
                self.events.push(BehaviourEvent::RPC(peer_id, rpc_event))
            }
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<IdentifyEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: IdentifyEvent) {
        match event {
            IdentifyEvent::Identified {
                peer_id, mut info, ..
            } => {
                if info.listen_addrs.len() > 20 {
                    debug!(
                        self.log,
                        "More than 20 peers have been identified, truncating"
                    );
                    info.listen_addrs.truncate(20);
                }
                self.events.push(BehaviourEvent::Identified(peer_id, info));
            }
            IdentifyEvent::Error { .. } => {}
            IdentifyEvent::SendBack { .. } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<PingEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, _event: PingEvent) {
        // not interested in ping responses at the moment.
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self {
        let local_peer_id = local_public_key.clone().into_peer_id();
        let identify_config = net_conf.identify_config.clone();
        let behaviour_log = log.new(o!());

        Behaviour {
            gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()),
            serenity_rpc: Rpc::new(log),
            identify: Identify::new(
                identify_config.version,
                identify_config.user_agent,
                local_public_key,
            ),
            ping: Ping::new(),
            events: Vec::new(),
            log: behaviour_log,
        }
    }

    /// Consumes the events list when polled.
    fn poll<TBehaviourIn>(
        &mut self,
    ) -> Async<NetworkBehaviourAction<TBehaviourIn, BehaviourEvent>> {
        if !self.events.is_empty() {
            return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0)));
        }

        Async::NotReady
    }
}

/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    /// Subscribes to a gossipsub topic.
    pub fn subscribe(&mut self, topic: Topic) -> bool {
        self.gossipsub.subscribe(topic)
    }

    /// Sends an RPC Request/Response via the RPC protocol.
    pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) {
        self.serenity_rpc.send_rpc(peer_id, rpc_event);
    }

    /// Publishes a message on the pubsub (gossipsub) behaviour.
    pub fn publish(&mut self, topics: Vec<Topic>, message: PubsubMessage) {
        let message_bytes = ssz_encode(&message);
        for topic in topics {
            self.gossipsub.publish(topic, message_bytes.clone());
        }
    }
}

/// The types of events than can be obtained from polling the behaviour.
pub enum BehaviourEvent {
    RPC(PeerId, RPCEvent),
    PeerDialed(PeerId),
    Identified(PeerId, IdentifyInfo),
    // TODO: This is a stub at the moment
    GossipMessage {
        source: PeerId,
        topics: Vec<TopicHash>,
        message: PubsubMessage,
    },
}

/// Messages that are passed to and from the pubsub (Gossipsub) behaviour.
#[derive(Debug, Clone)]
pub enum PubsubMessage {
    /// Gossipsub message providing notification of a new block.
    Block(BlockRootSlot),
    /// Gossipsub message providing notification of a new attestation.
    Attestation(Attestation),
}

//TODO: Correctly encode/decode enums. Prefixing with integer for now.
impl Encodable for PubsubMessage {
    fn ssz_append(&self, s: &mut SszStream) {
        match self {
            PubsubMessage::Block(block_gossip) => {
                0u32.ssz_append(s);
                block_gossip.ssz_append(s);
            }
            PubsubMessage::Attestation(attestation_gossip) => {
                1u32.ssz_append(s);
                attestation_gossip.ssz_append(s);
            }
        }
    }
}

impl Decodable for PubsubMessage {
    fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
        let (id, index) = u32::ssz_decode(bytes, index)?;
        match id {
            1 => {
                let (block, index) = BlockRootSlot::ssz_decode(bytes, index)?;
                Ok((PubsubMessage::Block(block), index))
            }
            2 => {
                let (attestation, index) = Attestation::ssz_decode(bytes, index)?;
                Ok((PubsubMessage::Attestation(attestation), index))
            }
            _ => Err(DecodeError::Invalid),
        }
    }
}
<commit_msg>Add logging<commit_after>use crate::rpc::methods::BlockRootSlot;
use crate::rpc::{RPCEvent, RPCMessage, Rpc};
use crate::NetworkConfig;
use futures::prelude::*;
use libp2p::{
    core::{
        swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess},
        PublicKey,
    },
    gossipsub::{Gossipsub, GossipsubEvent},
    identify::{protocol::IdentifyInfo, Identify, IdentifyEvent},
    ping::{Ping, PingEvent},
    tokio_io::{AsyncRead, AsyncWrite},
    NetworkBehaviour, PeerId,
};
use slog::{debug, o, warn};
use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream};
use ssz_derive::{Decode, Encode};
use types::Attestation;
use types::{Topic, TopicHash};

/// Builds the network behaviour for the libp2p Swarm.
/// Implements gossipsub message routing.
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")]
pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite> {
    /// The routing pub-sub mechanism for eth2.
    gossipsub: Gossipsub<TSubstream>,
    // TODO: Add Kademlia for peer discovery
    /// The events generated by this behaviour to be consumed in the swarm poll.
    serenity_rpc: Rpc<TSubstream>,
    /// Allows discovery of IP addresses for peers on the network.
    identify: Identify<TSubstream>,
    /// Keep regular connection to peers and disconnect if absent.
    // TODO: Keepalive, likely remove this later.
    // TODO: Make the ping time customizeable.
    ping: Ping<TSubstream>,
    #[behaviour(ignore)]
    events: Vec<BehaviourEvent>,
    /// Logger for behaviour actions.
    #[behaviour(ignore)]
    log: slog::Logger,
}

// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: GossipsubEvent) {
        match event {
            GossipsubEvent::Message(gs_msg) => {
                debug!(self.log, "Received GossipEvent"; "msg" => format!("{:?}", gs_msg));

                let pubsub_message = match PubsubMessage::ssz_decode(&gs_msg.data, 0) {
                    //TODO: Punish peer on error
                    Err(e) => {
                        warn!(
                            self.log,
                            "Received undecodable message from Peer {:?} error", gs_msg.source;
                            "error" => format!("{:?}", e)
                        );
                        return;
                    }
                    Ok((msg, _index)) => msg,
                };

                self.events.push(BehaviourEvent::GossipMessage {
                    source: gs_msg.source,
                    topics: gs_msg.topics,
                    message: pubsub_message,
                });
            }
            GossipsubEvent::Subscribed {
                peer_id: _,
                topic: _,
            }
            | GossipsubEvent::Unsubscribed {
                peer_id: _,
                topic: _,
            } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<RPCMessage>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: RPCMessage) {
        match event {
            RPCMessage::PeerDialed(peer_id) => {
                self.events.push(BehaviourEvent::PeerDialed(peer_id))
            }
            RPCMessage::RPC(peer_id, rpc_event) => {
                self.events.push(BehaviourEvent::RPC(peer_id, rpc_event))
            }
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<IdentifyEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, event: IdentifyEvent) {
        match event {
            IdentifyEvent::Identified {
                peer_id, mut info, ..
            } => {
                if info.listen_addrs.len() > 20 {
                    debug!(
                        self.log,
                        "More than 20 peers have been identified, truncating"
                    );
                    info.listen_addrs.truncate(20);
                }
                self.events.push(BehaviourEvent::Identified(peer_id, info));
            }
            IdentifyEvent::Error { .. } => {}
            IdentifyEvent::SendBack { .. } => {}
        }
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<PingEvent>
    for Behaviour<TSubstream>
{
    fn inject_event(&mut self, _event: PingEvent) {
        // not interested in ping responses at the moment.
    }
}

impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self {
        let local_peer_id = local_public_key.clone().into_peer_id();
        let identify_config = net_conf.identify_config.clone();
        let behaviour_log = log.new(o!());

        Behaviour {
            gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()),
            serenity_rpc: Rpc::new(log),
            identify: Identify::new(
                identify_config.version,
                identify_config.user_agent,
                local_public_key,
            ),
            ping: Ping::new(),
            events: Vec::new(),
            log: behaviour_log,
        }
    }

    /// Consumes the events list when polled.
    fn poll<TBehaviourIn>(
        &mut self,
    ) -> Async<NetworkBehaviourAction<TBehaviourIn, BehaviourEvent>> {
        if !self.events.is_empty() {
            return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0)));
        }

        Async::NotReady
    }
}

/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
    /// Subscribes to a gossipsub topic.
    pub fn subscribe(&mut self, topic: Topic) -> bool {
        self.gossipsub.subscribe(topic)
    }

    /// Sends an RPC Request/Response via the RPC protocol.
    pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) {
        self.serenity_rpc.send_rpc(peer_id, rpc_event);
    }

    /// Publishes a message on the pubsub (gossipsub) behaviour.
    pub fn publish(&mut self, topics: Vec<Topic>, message: PubsubMessage) {
        let message_bytes = ssz_encode(&message);
        for topic in topics {
            self.gossipsub.publish(topic, message_bytes.clone());
        }
    }
}

/// The types of events than can be obtained from polling the behaviour.
pub enum BehaviourEvent {
    RPC(PeerId, RPCEvent),
    PeerDialed(PeerId),
    Identified(PeerId, IdentifyInfo),
    // TODO: This is a stub at the moment
    GossipMessage {
        source: PeerId,
        topics: Vec<TopicHash>,
        message: PubsubMessage,
    },
}

/// Messages that are passed to and from the pubsub (Gossipsub) behaviour.
#[derive(Debug, Clone)]
pub enum PubsubMessage {
    /// Gossipsub message providing notification of a new block.
    Block(BlockRootSlot),
    /// Gossipsub message providing notification of a new attestation.
    Attestation(Attestation),
}

//TODO: Correctly encode/decode enums. Prefixing with integer for now.
impl Encodable for PubsubMessage {
    fn ssz_append(&self, s: &mut SszStream) {
        match self {
            PubsubMessage::Block(block_gossip) => {
                0u32.ssz_append(s);
                block_gossip.ssz_append(s);
            }
            PubsubMessage::Attestation(attestation_gossip) => {
                1u32.ssz_append(s);
                attestation_gossip.ssz_append(s);
            }
        }
    }
}

impl Decodable for PubsubMessage {
    fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> {
        let (id, index) = u32::ssz_decode(bytes, index)?;
        match id {
            1 => {
                let (block, index) = BlockRootSlot::ssz_decode(bytes, index)?;
                Ok((PubsubMessage::Block(block), index))
            }
            2 => {
                let (attestation, index) = Attestation::ssz_decode(bytes, index)?;
                Ok((PubsubMessage::Attestation(attestation), index))
            }
            _ => Err(DecodeError::Invalid),
        }
    }
}
<|endoftext|>


<commit_before>use nom;
use nom::{alpha, alphanumeric, digit, multispace};
use std::str::from_utf8;

// Identifier
// IDENT := /[a-zA-Z_]\w*/g
named!(ident<&str, &str>, re_match!(r"[a-zA-Z_]\w*[\?\!]*"));

// Whitespace
named!(whitespace, chain!(many0!(multispace), || { &b""[..] }));
<commit_msg>Update ident regex<commit_after>use nom;
use nom::{alpha, alphanumeric, digit, multispace};
use std::str::from_utf8;

// Identifier
// IDENT := /[a-zA-Z_]\w*/g
named!(ident<&str, &str>, re_match!(r"[a-zA-Z_](?:\w|[\?\!])*"));

// Whitespace
named!(whitespace, chain!(many0!(multispace), || { &b""[..] }));
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use clone::Clone;
use default::Default;
use hash;

/// A trait representing stateful hashes which can be used to hash keys in a
/// `HashMap`.
///
/// A HashState is used as a factory for instances of `Hasher` which a `HashMap`
/// can then use to hash keys independently. A `HashMap` by default uses a state
/// which will create instances of a `SipHasher`, but a custom state factory can
/// be provided to the `with_hash_state` function.
///
/// If a hashing algorithm has no initial state, then the `Hasher` type for that
/// algorithm can implement the `Default` trait and create hash maps with the
/// `DefaultState` structure. This state is 0-sized and will simply delegate
/// to `Default` when asked to create a hasher.
#[unstable(feature = "std_misc", reason = "hasher stuff is unclear")]
pub trait HashState {
    type Hasher: hash::Hasher;

    /// Creates a new hasher based on the given state of this object.
    fn hasher(&self) -> Self::Hasher;
}

/// A structure which is a factory for instances of `Hasher` which implement the
/// default trait.
///
/// This struct has is 0-sized and does not need construction.
#[unstable(feature = "std_misc", reason = "hasher stuff is unclear")]
pub struct DefaultState<H>;

impl<H: Default + hash::Hasher> HashState for DefaultState<H> {
    type Hasher = H;
    fn hasher(&self) -> H { Default::default() }
}

impl<H> Clone for DefaultState<H> {
    fn clone(&self) -> DefaultState<H> { DefaultState }
}

impl<H> Default for DefaultState<H> {
    fn default() -> DefaultState<H> { DefaultState }
}
<commit_msg>Fallout: add phantomdata to hash<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use clone::Clone;
use default::Default;
use hash;
use marker;

/// A trait representing stateful hashes which can be used to hash keys in a
/// `HashMap`.
///
/// A HashState is used as a factory for instances of `Hasher` which a `HashMap`
/// can then use to hash keys independently. A `HashMap` by default uses a state
/// which will create instances of a `SipHasher`, but a custom state factory can
/// be provided to the `with_hash_state` function.
///
/// If a hashing algorithm has no initial state, then the `Hasher` type for that
/// algorithm can implement the `Default` trait and create hash maps with the
/// `DefaultState` structure. This state is 0-sized and will simply delegate
/// to `Default` when asked to create a hasher.
#[unstable(feature = "std_misc", reason = "hasher stuff is unclear")]
pub trait HashState {
    type Hasher: hash::Hasher;

    /// Creates a new hasher based on the given state of this object.
    fn hasher(&self) -> Self::Hasher;
}

/// A structure which is a factory for instances of `Hasher` which implement the
/// default trait.
///
/// This struct has is 0-sized and does not need construction.
#[unstable(feature = "std_misc", reason = "hasher stuff is unclear")]
pub struct DefaultState<H>(marker::PhantomData<H>);

impl<H: Default + hash::Hasher> HashState for DefaultState<H> {
    type Hasher = H;
    fn hasher(&self) -> H { Default::default() }
}

impl<H> Clone for DefaultState<H> {
    fn clone(&self) -> DefaultState<H> { DefaultState(marker::PhantomData) }
}

impl<H> Default for DefaultState<H> {
    fn default() -> DefaultState<H> { DefaultState(marker::PhantomData) }
}
<|endoftext|>


<commit_before>use memory::Memory;
use std::boxed::Box;
use std::vec::Vec;

/// Simple slice-based memory implementation.
///
/// # Examples
/// ```
/// use osciemu::memory::Memory;
/// use osciemu::memory::SliceMemory;
///
/// let mut m = SliceMemory::new(16);
/// assert_eq!(m.get(0), 0);
/// m.set(8, 123);
/// assert_eq!(m.get(8), 123);
/// ```
///
/// # Panics
/// Just like a slice, `SliceMemory` panics when accessed out of bounds.
///
/// ```
/// # use std::panic;
/// # use osciemu::memory::Memory;
/// # use osciemu::memory::SliceMemory;
/// let mut m = SliceMemory::new(16);
/// let result = panic::catch_unwind(|| {
///     m.get(17); // Out-of-bounds!
/// });
/// assert!(result.is_err());
/// ```
pub struct SliceMemory(Box<[u32]>);

impl SliceMemory {
    pub fn new(size: usize) -> SliceMemory {
        let mut v = Vec::with_capacity(size);
        for _ in 0..size {
            v.push(0)
        }
        SliceMemory(v.into_boxed_slice())
    }

    // Like `new()` but writes `value` to every cell
    pub fn with_value(size: usize, value: u32) -> SliceMemory {
        let mut sm = SliceMemory::new(size);
        for i in 0..size {
            sm.set(i, value);
        }
        sm
    }
}

impl Memory for SliceMemory {
    fn get(&self, addr: usize) -> u32 {
        self.0[addr]
    }
    fn set(&mut self, addr: usize, v: u32) {
        self.0[addr] = v;
    }
    fn size(&self) -> usize {
        self.0.len()
    }
}

#[cfg(test)]
mod test {
    use memory::Memory;

    #[test]
    fn get_and_set() {
        let mut m = super::SliceMemory::new(16);
        for i in 0..16 {
            m.set(i, i as u32);
        }
        for i in 0..16 {
            assert_eq!(m.get(i), i as u32);
        }
    }

    #[test]
    fn with_value() {
        let m = super::SliceMemory::with_value(16, 9);
        for i in 0..m.size() {
            assert_eq!(m.get(i), 9);
        }
    }

    #[test]
    fn size() {
        let m1 = super::SliceMemory::new(16);
        assert_eq!(m1.size(), 16);

        let m2 = super::SliceMemory::with_value(16, 1);
        assert_eq!(m2.size(), 16);
    }
}
<commit_msg>docs(slicememory): Adjust import styling in example<commit_after>use memory::Memory;
use std::boxed::Box;
use std::vec::Vec;

/// Simple slice-based memory implementation.
///
/// # Examples
/// ```
/// use osciemu::memory::Memory;
/// use osciemu::memory::SliceMemory;
///
/// let mut m = SliceMemory::new(16);
/// assert_eq!(m.get(0), 0);
/// m.set(8, 123);
/// assert_eq!(m.get(8), 123);
/// ```
///
/// # Panics
/// Just like a slice, `SliceMemory` panics when accessed out of bounds.
///
/// ```
/// # use std::panic;
/// # use osciemu::memory::{Memory, SliceMemory};
/// let mut m = SliceMemory::new(16);
/// let result = panic::catch_unwind(|| {
///     m.get(17); // Out-of-bounds!
/// });
/// assert!(result.is_err());
/// ```
pub struct SliceMemory(Box<[u32]>);

impl SliceMemory {
    pub fn new(size: usize) -> SliceMemory {
        let mut v = Vec::with_capacity(size);
        for _ in 0..size {
            v.push(0)
        }
        SliceMemory(v.into_boxed_slice())
    }

    // Like `new()` but writes `value` to every cell
    pub fn with_value(size: usize, value: u32) -> SliceMemory {
        let mut sm = SliceMemory::new(size);
        for i in 0..size {
            sm.set(i, value);
        }
        sm
    }
}

impl Memory for SliceMemory {
    fn get(&self, addr: usize) -> u32 {
        self.0[addr]
    }
    fn set(&mut self, addr: usize, v: u32) {
        self.0[addr] = v;
    }
    fn size(&self) -> usize {
        self.0.len()
    }
}

#[cfg(test)]
mod test {
    use memory::Memory;

    #[test]
    fn get_and_set() {
        let mut m = super::SliceMemory::new(16);
        for i in 0..16 {
            m.set(i, i as u32);
        }
        for i in 0..16 {
            assert_eq!(m.get(i), i as u32);
        }
    }

    #[test]
    fn with_value() {
        let m = super::SliceMemory::with_value(16, 9);
        for i in 0..m.size() {
            assert_eq!(m.get(i), 9);
        }
    }

    #[test]
    fn size() {
        let m1 = super::SliceMemory::new(16);
        assert_eq!(m1.size(), 16);

        let m2 = super::SliceMemory::with_value(16, 1);
        assert_eq!(m2.size(), 16);
    }
}
<|endoftext|>


<commit_before>use std::cell::{Cell, RefCell};

use cairo::{self, ImageSurface, MatrixTrait};
use markup5ever::{local_name, LocalName};

use crate::drawing_ctx::DrawingCtx;
use crate::error::{AttributeResultExt, NodeError};
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{self, ParseError};
use crate::property_bag::PropertyBag;
use crate::surface_utils::{iterators::Pixels, shared_surface::SharedImageSurface};

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{Filter, FilterError, Input, PrimitiveWithInput};

/// Enumeration of the color channels the displacement map can source.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum ColorChannel {
    R,
    G,
    B,
    A,
}

/// The `feDisplacementMap` filter primitive.
pub struct DisplacementMap {
    base: PrimitiveWithInput,
    in2: RefCell<Option<Input>>,
    scale: Cell<f64>,
    x_channel_selector: Cell<ColorChannel>,
    y_channel_selector: Cell<ColorChannel>,
}

impl Default for DisplacementMap {
    /// Constructs a new `DisplacementMap` with empty properties.
    #[inline]
    fn default() -> DisplacementMap {
        DisplacementMap {
            base: PrimitiveWithInput::new::<Self>(),
            in2: RefCell::new(None),
            scale: Cell::new(0.0),
            x_channel_selector: Cell::new(ColorChannel::A),
            y_channel_selector: Cell::new(ColorChannel::A),
        }
    }
}

impl NodeTrait for DisplacementMap {
    impl_node_as_filter!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("in2") => {
                    self.in2.replace(Some(Input::parse(attr, value)?));
                }
                local_name!("scale") => self.scale.set(
                    parsers::number(value).attribute(attr)?,
                ),
                local_name!("xChannelSelector") => self
                    .x_channel_selector
                    .set(ColorChannel::parse(attr, value)?),
                local_name!("yChannelSelector") => self
                    .y_channel_selector
                    .set(ColorChannel::parse(attr, value)?),
                _ => (),
            }
        }

        Ok(())
    }
}

impl Filter for DisplacementMap {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let displacement_input = ctx.get_input(draw_ctx, self.in2.borrow().as_ref())?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .add_input(&displacement_input)
            .into_irect(draw_ctx);

        // Displacement map's values need to be non-premultiplied.
        let displacement_surface = displacement_input.surface().unpremultiply(bounds)?;

        let scale = self.scale.get();
        let (sx, sy) = ctx.paffine().transform_distance(scale, scale);

        let x_channel = self.x_channel_selector.get();
        let y_channel = self.y_channel_selector.get();

        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        {
            let cr = cairo::Context::new(&output_surface);

            for (x, y, displacement_pixel) in Pixels::new(&displacement_surface, bounds) {
                let get_value = |channel| match channel {
                    ColorChannel::R => displacement_pixel.r,
                    ColorChannel::G => displacement_pixel.g,
                    ColorChannel::B => displacement_pixel.b,
                    ColorChannel::A => displacement_pixel.a,
                };

                let process = |x| f64::from(x) / 255.0 - 0.5;

                let dx = process(get_value(x_channel));
                let dy = process(get_value(y_channel));

                let x = f64::from(x);
                let y = f64::from(y);
                let ox = sx * dx;
                let oy = sy * dy;

                // Doing this in a loop doesn't look too bad performance wise, and allows not to
                // manually implement bilinear or other interpolation.
                cr.rectangle(x, y, 1.0, 1.0);
                cr.reset_clip();
                cr.clip();

                input.surface().set_as_source_surface(&cr, -ox, -oy);
                cr.paint();
            }
        }

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, input.surface().surface_type())?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        // Performance TODO: this converts in back and forth to linear RGB while technically it's
        // only needed for in2.
        true
    }
}

impl ColorChannel {
    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
        match s {
            "R" => Ok(ColorChannel::R),
            "G" => Ok(ColorChannel::G),
            "B" => Ok(ColorChannel::B),
            "A" => Ok(ColorChannel::A),
            _ => Err(NodeError::parse_error(
                attr,
                ParseError::new("invalid value"),
            )),
        }
    }
}
<commit_msg>filters/displacement_map: remove interior mutability<commit_after>use cairo::{self, ImageSurface, MatrixTrait};
use markup5ever::{local_name, LocalName};

use crate::drawing_ctx::DrawingCtx;
use crate::error::{AttributeResultExt, NodeError};
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{self, ParseError};
use crate::property_bag::PropertyBag;
use crate::surface_utils::{iterators::Pixels, shared_surface::SharedImageSurface};

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{Filter, FilterError, Input, PrimitiveWithInput};

/// Enumeration of the color channels the displacement map can source.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum ColorChannel {
    R,
    G,
    B,
    A,
}

/// The `feDisplacementMap` filter primitive.
pub struct DisplacementMap {
    base: PrimitiveWithInput,
    in2: Option<Input>,
    scale: f64,
    x_channel_selector: ColorChannel,
    y_channel_selector: ColorChannel,
}

impl Default for DisplacementMap {
    /// Constructs a new `DisplacementMap` with empty properties.
    #[inline]
    fn default() -> DisplacementMap {
        DisplacementMap {
            base: PrimitiveWithInput::new::<Self>(),
            in2: None,
            scale: 0.0,
            x_channel_selector: ColorChannel::A,
            y_channel_selector: ColorChannel::A,
        }
    }
}

impl NodeTrait for DisplacementMap {
    impl_node_as_filter!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("in2") => self.in2 = Some(Input::parse(attr, value)?),
                local_name!("scale") => self.scale = parsers::number(value).attribute(attr)?,
                local_name!("xChannelSelector") => {
                    self.x_channel_selector = ColorChannel::parse(attr, value)?
                }
                local_name!("yChannelSelector") => {
                    self.y_channel_selector = ColorChannel::parse(attr, value)?
                }
                _ => (),
            }
        }

        Ok(())
    }
}

impl Filter for DisplacementMap {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, draw_ctx)?;
        let displacement_input = ctx.get_input(draw_ctx, self.in2.as_ref())?;
        let bounds = self
            .base
            .get_bounds(ctx)
            .add_input(&input)
            .add_input(&displacement_input)
            .into_irect(draw_ctx);

        // Displacement map's values need to be non-premultiplied.
        let displacement_surface = displacement_input.surface().unpremultiply(bounds)?;

        let (sx, sy) = ctx.paffine().transform_distance(self.scale, self.scale);

        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        {
            let cr = cairo::Context::new(&output_surface);

            for (x, y, displacement_pixel) in Pixels::new(&displacement_surface, bounds) {
                let get_value = |channel| match channel {
                    ColorChannel::R => displacement_pixel.r,
                    ColorChannel::G => displacement_pixel.g,
                    ColorChannel::B => displacement_pixel.b,
                    ColorChannel::A => displacement_pixel.a,
                };

                let process = |x| f64::from(x) / 255.0 - 0.5;

                let dx = process(get_value(self.x_channel_selector));
                let dy = process(get_value(self.y_channel_selector));

                let x = f64::from(x);
                let y = f64::from(y);
                let ox = sx * dx;
                let oy = sy * dy;

                // Doing this in a loop doesn't look too bad performance wise, and allows not to
                // manually implement bilinear or other interpolation.
                cr.rectangle(x, y, 1.0, 1.0);
                cr.reset_clip();
                cr.clip();

                input.surface().set_as_source_surface(&cr, -ox, -oy);
                cr.paint();
            }
        }

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: SharedImageSurface::new(output_surface, input.surface().surface_type())?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        // Performance TODO: this converts in back and forth to linear RGB while technically it's
        // only needed for in2.
        true
    }
}

impl ColorChannel {
    fn parse(attr: LocalName, s: &str) -> Result<Self, NodeError> {
        match s {
            "R" => Ok(ColorChannel::R),
            "G" => Ok(ColorChannel::G),
            "B" => Ok(ColorChannel::B),
            "A" => Ok(ColorChannel::A),
            _ => Err(NodeError::parse_error(
                attr,
                ParseError::new("invalid value"),
            )),
        }
    }
}
<|endoftext|>


<commit_before>#![feature(rustc_private)]

extern crate rustc_codegen_ssa;
extern crate rustc_errors;
extern crate rustc_middle;
#[macro_use]
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_symbol_mangling;
extern crate rustc_target;

use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_data_structures::owning_ref::OwningRef;
use rustc_data_structures::sync::MetadataRef;
use rustc_errors::ErrorReported;
use rustc_middle::dep_graph::DepGraph;
use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader, MetadataLoaderDyn};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::OutputFilenames;
use rustc_session::Session;
use rustc_span::symbol::Symbol;
use rustc_target::spec::Target;
use std::any::Any;
use std::path::Path;

pub struct NoLlvmMetadataLoader;

impl MetadataLoader for NoLlvmMetadataLoader {
    fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> {
        let buf =
            std::fs::read(filename).map_err(|e| format!("metadata file open err: {:?}", e))?;
        let buf: OwningRef<Vec<u8>, [u8]> = OwningRef::new(buf);
        Ok(rustc_erase_owner!(buf.map_owner_box()))
    }

    fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<MetadataRef, String> {
        self.get_rlib_metadata(target, filename)
    }
}

struct TheBackend;

impl CodegenBackend for TheBackend {
    fn metadata_loader(&self) -> Box<MetadataLoaderDyn> {
        Box::new(NoLlvmMetadataLoader)
    }

    fn provide(&self, providers: &mut Providers) {
        rustc_symbol_mangling::provide(providers);

        providers.supported_target_features = |tcx, _cnum| {
            Default::default() // Just a dummy
        };
        providers.is_reachable_non_generic = |_tcx, _defid| true;
        providers.exported_symbols = |_tcx, _crate| &[];
    }

    fn provide_extern(&self, providers: &mut Providers) {
        providers.is_reachable_non_generic = |_tcx, _defid| true;
    }

    fn codegen_crate<'a, 'tcx>(
        &self,
        tcx: TyCtxt<'tcx>,
        _metadata: EncodedMetadata,
        _need_metadata_module: bool,
    ) -> Box<dyn Any> {
        use rustc_hir::def_id::LOCAL_CRATE;

        Box::new(tcx.crate_name(LOCAL_CRATE) as Symbol)
    }

    fn join_codegen(
        &self,
        ongoing_codegen: Box<dyn Any>,
        _sess: &Session,
        _dep_graph: &DepGraph,
    ) -> Result<Box<dyn Any>, ErrorReported> {
        let crate_name = ongoing_codegen
            .downcast::<Symbol>()
            .expect("in join_codegen: ongoing_codegen is not a Symbol");
        Ok(crate_name)
    }

    fn link(
        &self,
        sess: &Session,
        codegen_results: Box<dyn Any>,
        outputs: &OutputFilenames,
    ) -> Result<(), ErrorReported> {
        use rustc_session::{config::CrateType, output::out_filename};
        use std::io::Write;
        let crate_name =
            codegen_results.downcast::<Symbol>().expect("in link: codegen_results is not a Symbol");
        for &crate_type in sess.opts.crate_types.iter() {
            if crate_type != CrateType::Rlib {
                sess.fatal(&format!("Crate type is {:?}", crate_type));
            }
            let output_name = out_filename(sess, crate_type, &outputs, &*crate_name.as_str());
            let mut out_file = ::std::fs::File::create(output_name).unwrap();
            write!(out_file, "This has been \"compiled\" successfully.").unwrap();
        }
        Ok(())
    }
}

/// This is the entrypoint for a hot plugged rustc_codegen_llvm
#[no_mangle]
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
    Box::new(TheBackend)
}
<commit_msg>Fix hotplug_codegen_backend test<commit_after>#![feature(rustc_private)]

extern crate rustc_codegen_ssa;
extern crate rustc_errors;
extern crate rustc_middle;
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_symbol_mangling;
extern crate rustc_target;

use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_codegen_ssa::{CodegenResults, CrateInfo};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::MetadataRef;
use rustc_errors::ErrorReported;
use rustc_middle::dep_graph::DepGraph;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader, MetadataLoaderDyn};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::OutputFilenames;
use rustc_session::Session;
use rustc_target::spec::Target;
use std::any::Any;
use std::path::Path;

pub struct NoLlvmMetadataLoader;

impl MetadataLoader for NoLlvmMetadataLoader {
    fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> {
        unreachable!("some_crate.rs shouldn't depend on any external crates");
    }

    fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<MetadataRef, String> {
        unreachable!("some_crate.rs shouldn't depend on any external crates");
    }
}

struct TheBackend;

impl CodegenBackend for TheBackend {
    fn metadata_loader(&self) -> Box<MetadataLoaderDyn> {
        Box::new(NoLlvmMetadataLoader)
    }

    fn provide(&self, providers: &mut Providers) {}
    fn provide_extern(&self, providers: &mut Providers) {}

    fn codegen_crate<'a, 'tcx>(
        &self,
        tcx: TyCtxt<'tcx>,
        metadata: EncodedMetadata,
        _need_metadata_module: bool,
    ) -> Box<dyn Any> {
        use rustc_hir::def_id::LOCAL_CRATE;

        Box::new(CodegenResults {
            crate_name: tcx.crate_name(LOCAL_CRATE),
            modules: vec![],
            allocator_module: None,
            metadata_module: None,
            metadata,
            windows_subsystem: None,
            linker_info: LinkerInfo::new(tcx),
            crate_info: CrateInfo::new(tcx),
        })
    }

    fn join_codegen(
        &self,
        ongoing_codegen: Box<dyn Any>,
        _sess: &Session,
    ) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorReported> {
        let codegen_results = ongoing_codegen
            .downcast::<CodegenResults>()
            .expect("in join_codegen: ongoing_codegen is not a CodegenResults");
        Ok((*codegen_results, FxHashMap::default()))
    }

    fn link(
        &self,
        sess: &Session,
        codegen_results: CodegenResults,
        outputs: &OutputFilenames,
    ) -> Result<(), ErrorReported> {
        use rustc_session::{config::CrateType, output::out_filename};
        use std::io::Write;
        let crate_name = codegen_results.crate_name;
        for &crate_type in sess.opts.crate_types.iter() {
            if crate_type != CrateType::Rlib {
                sess.fatal(&format!("Crate type is {:?}", crate_type));
            }
            let output_name = out_filename(sess, crate_type, &outputs, &*crate_name.as_str());
            let mut out_file = ::std::fs::File::create(output_name).unwrap();
            write!(out_file, "This has been \"compiled\" successfully.").unwrap();
        }
        Ok(())
    }
}

/// This is the entrypoint for a hot plugged rustc_codegen_llvm
#[no_mangle]
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
    Box::new(TheBackend)
}
<|endoftext|>


<commit_before>use anyhow::*;
use diesel;
use diesel::prelude::*;
use rayon::prelude::*;
use std::path::Path;

use crate::app::vfs;
use crate::db::{directories, songs, DB};

const INDEX_BUILDING_CLEAN_BUFFER_SIZE: usize = 500; // Deletions in each transaction

pub struct Cleaner {
	db: DB,
	vfs_manager: vfs::Manager,
}

impl Cleaner {
	pub fn new(db: DB, vfs_manager: vfs::Manager) -> Self {
		Self { db, vfs_manager }
	}

	pub fn clean(&self) -> Result<()> {
		let vfs = self.vfs_manager.get_vfs()?;

		let all_directories: Vec<String> = {
			let connection = self.db.connect()?;
			directories::table
				.select(directories::path)
				.load(&connection)?
		};

		let all_songs: Vec<String> = {
			let connection = self.db.connect()?;
			songs::table.select(songs::path).load(&connection)?
		};

		let list_missing_directories = || {
			all_directories
				.par_iter()
				.filter(|ref directory_path| {
					let path = Path::new(&directory_path);
					!path.exists() || vfs.real_to_virtual(path).is_err()
				})
				.collect::<Vec<_>>()
		};

		let list_missing_songs = || {
			all_songs
				.par_iter()
				.filter(|ref song_path| {
					let path = Path::new(&song_path);
					!path.exists() || vfs.real_to_virtual(path).is_err()
				})
				.collect::<Vec<_>>()
		};

		let thread_pool = rayon::ThreadPoolBuilder::new().build()?;
		let (missing_songs, missing_directories) =
			thread_pool.join(list_missing_directories, list_missing_songs);

		{
			let connection = self.db.connect()?;
			for chunk in missing_directories[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(directories::table.filter(directories::path.eq_any(chunk)))
					.execute(&connection)?;
			}
			for chunk in missing_songs[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(songs::table.filter(songs::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}

		Ok(())
	}
}
<commit_msg>Fixed a bug where missing content was not removed from database<commit_after>use anyhow::*;
use diesel;
use diesel::prelude::*;
use rayon::prelude::*;
use std::path::Path;

use crate::app::vfs;
use crate::db::{directories, songs, DB};

const INDEX_BUILDING_CLEAN_BUFFER_SIZE: usize = 500; // Deletions in each transaction

pub struct Cleaner {
	db: DB,
	vfs_manager: vfs::Manager,
}

impl Cleaner {
	pub fn new(db: DB, vfs_manager: vfs::Manager) -> Self {
		Self { db, vfs_manager }
	}

	pub fn clean(&self) -> Result<()> {
		let vfs = self.vfs_manager.get_vfs()?;

		let all_directories: Vec<String> = {
			let connection = self.db.connect()?;
			directories::table
				.select(directories::path)
				.load(&connection)?
		};

		let all_songs: Vec<String> = {
			let connection = self.db.connect()?;
			songs::table.select(songs::path).load(&connection)?
		};

		let list_missing_directories = || {
			all_directories
				.par_iter()
				.filter(|ref directory_path| {
					let path = Path::new(&directory_path);
					!path.exists() || vfs.real_to_virtual(path).is_err()
				})
				.collect::<Vec<_>>()
		};

		let list_missing_songs = || {
			all_songs
				.par_iter()
				.filter(|ref song_path| {
					let path = Path::new(&song_path);
					!path.exists() || vfs.real_to_virtual(path).is_err()
				})
				.collect::<Vec<_>>()
		};

		let thread_pool = rayon::ThreadPoolBuilder::new().build()?;
		let (missing_directories, missing_songs) =
			thread_pool.join(list_missing_directories, list_missing_songs);

		{
			let connection = self.db.connect()?;
			for chunk in missing_directories[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(directories::table.filter(directories::path.eq_any(chunk)))
					.execute(&connection)?;
			}
			for chunk in missing_songs[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(songs::table.filter(songs::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}

		Ok(())
	}
}
<|endoftext|>


<commit_before>//! A module to deserialize a response from incorrectly specified endpoint:
//!
//! - [PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}](https://spec.matrix.org/v1.2/server-server-api/#put_matrixfederationv1send_joinroomideventid)
//! - [PUT /_matrix/federation/v1/invite/{roomId}/{eventId}](https://spec.matrix.org/v1.2/server-server-api/#put_matrixfederationv1inviteroomideventid)
//! - [PUT /_matrix/federation/v1/send_leave/{roomId}/{eventId}](https://spec.matrix.org/v1.2/server-server-api/#put_matrixfederationv1send_leaveroomideventid)
//!
//! For more information, see this [GitHub issue][issue].
//!
//! [issue]: https://github.com/matrix-org/matrix-spec-proposals/issues/2541

use std::{fmt, marker::PhantomData};

use serde::{
    de::{Deserialize, Deserializer, Error, IgnoredAny, SeqAccess, Visitor},
    ser::{Serialize, SerializeSeq, Serializer},
};

pub fn serialize<T, S>(val: &T, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
    T: Serialize,
{
    let mut seq = serializer.serialize_seq(Some(2))?;
    seq.serialize_element(&200)?;
    seq.serialize_element(val)?;
    seq.end()
}

pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
    D: Deserializer<'de>,
    T: Deserialize<'de>,
{
    deserializer.deserialize_seq(PduVisitor { phantom: PhantomData })
}

struct PduVisitor<T> {
    phantom: PhantomData<T>,
}

impl<'de, T> Visitor<'de> for PduVisitor<T>
where
    T: Deserialize<'de>,
{
    type Value = T;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("a PDU wrapped in an array.")
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    where
        A: SeqAccess<'de>,
    {
        let expected = "a two-element list in the response";
        if seq.next_element::<IgnoredAny>()?.is_none() {
            return Err(A::Error::invalid_length(0, &expected));
        }

        let val = seq.next_element()?.ok_or_else(|| A::Error::invalid_length(1, &expected))?;

        while let Some(IgnoredAny) = seq.next_element()? {
            // ignore extra elements
        }

        Ok(val)
    }
}

#[cfg(not(feature = "unstable-pre-spec"))]
#[cfg(test)]
mod tests {
    use assert_matches::assert_matches;
    use serde_json::{json, to_value as to_json_value};

    use super::{deserialize, serialize};
    use crate::membership::create_join_event::RoomState;

    #[test]
    fn deserialize_response() {
        let response = json!([
            200,
            {
                "origin": "example.com",
                "auth_chain": [],
                "state": []
            }
        ]);

        let RoomState { origin, auth_chain, state } = deserialize(response).unwrap();
        assert_eq!(origin, "example.com");
        assert_matches!(auth_chain.as_slice(), []);
        assert_matches!(state.as_slice(), []);
    }

    #[test]
    fn serialize_response() {
        let room_state =
            RoomState { origin: "matrix.org".into(), auth_chain: Vec::new(), state: Vec::new() };

        let serialized = serialize(&room_state, serde_json::value::Serializer).unwrap();
        let expected = to_json_value(&json!(
            [
                200,
                {
                    "origin": "matrix.org",
                    "auth_chain": [],
                    "state": []
                }
            ]
        ))
        .unwrap();

        assert_eq!(serialized, expected);
    }

    #[test]
    fn too_short_array() {
        let json = json!([200]);
        let failed_room_state = deserialize::<RoomState, _>(json);
        assert_eq!(
            failed_room_state.unwrap_err().to_string(),
            "invalid length 1, expected a two-element list in the response"
        );
    }

    #[test]
    fn not_an_array() {
        let json = json!({
            "origin": "matrix.org",
            "auth_chain": [],
            "state": []
        });
        let failed_room_state = deserialize::<RoomState, _>(json);

        assert_eq!(
            failed_room_state.unwrap_err().to_string(),
            "invalid type: map, expected a PDU wrapped in an array.",
        )
    }

    #[test]
    fn too_long_array() {
        let json = json!([200, { "origin": "", "auth_chain": [], "state": [] }, 200]);
        let RoomState { origin, auth_chain, state } = deserialize(json).unwrap();
        assert_eq!(origin, "");
        assert_matches!(auth_chain.as_slice(), []);
        assert_matches!(state.as_slice(), []);
    }
}
<commit_msg>Add another missing semicolon<commit_after>//! A module to deserialize a response from incorrectly specified endpoint:
//!
//! - [PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}](https://spec.matrix.org/v1.2/server-server-api/#put_matrixfederationv1send_joinroomideventid)
//! - [PUT /_matrix/federation/v1/invite/{roomId}/{eventId}](https://spec.matrix.org/v1.2/server-server-api/#put_matrixfederationv1inviteroomideventid)
//! - [PUT /_matrix/federation/v1/send_leave/{roomId}/{eventId}](https://spec.matrix.org/v1.2/server-server-api/#put_matrixfederationv1send_leaveroomideventid)
//!
//! For more information, see this [GitHub issue][issue].
//!
//! [issue]: https://github.com/matrix-org/matrix-spec-proposals/issues/2541

use std::{fmt, marker::PhantomData};

use serde::{
    de::{Deserialize, Deserializer, Error, IgnoredAny, SeqAccess, Visitor},
    ser::{Serialize, SerializeSeq, Serializer},
};

pub fn serialize<T, S>(val: &T, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
    T: Serialize,
{
    let mut seq = serializer.serialize_seq(Some(2))?;
    seq.serialize_element(&200)?;
    seq.serialize_element(val)?;
    seq.end()
}

pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
    D: Deserializer<'de>,
    T: Deserialize<'de>,
{
    deserializer.deserialize_seq(PduVisitor { phantom: PhantomData })
}

struct PduVisitor<T> {
    phantom: PhantomData<T>,
}

impl<'de, T> Visitor<'de> for PduVisitor<T>
where
    T: Deserialize<'de>,
{
    type Value = T;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("a PDU wrapped in an array.")
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    where
        A: SeqAccess<'de>,
    {
        let expected = "a two-element list in the response";
        if seq.next_element::<IgnoredAny>()?.is_none() {
            return Err(A::Error::invalid_length(0, &expected));
        }

        let val = seq.next_element()?.ok_or_else(|| A::Error::invalid_length(1, &expected))?;

        while let Some(IgnoredAny) = seq.next_element()? {
            // ignore extra elements
        }

        Ok(val)
    }
}

#[cfg(not(feature = "unstable-pre-spec"))]
#[cfg(test)]
mod tests {
    use assert_matches::assert_matches;
    use serde_json::{json, to_value as to_json_value};

    use super::{deserialize, serialize};
    use crate::membership::create_join_event::RoomState;

    #[test]
    fn deserialize_response() {
        let response = json!([
            200,
            {
                "origin": "example.com",
                "auth_chain": [],
                "state": []
            }
        ]);

        let RoomState { origin, auth_chain, state } = deserialize(response).unwrap();
        assert_eq!(origin, "example.com");
        assert_matches!(auth_chain.as_slice(), []);
        assert_matches!(state.as_slice(), []);
    }

    #[test]
    fn serialize_response() {
        let room_state =
            RoomState { origin: "matrix.org".into(), auth_chain: Vec::new(), state: Vec::new() };

        let serialized = serialize(&room_state, serde_json::value::Serializer).unwrap();
        let expected = to_json_value(&json!(
            [
                200,
                {
                    "origin": "matrix.org",
                    "auth_chain": [],
                    "state": []
                }
            ]
        ))
        .unwrap();

        assert_eq!(serialized, expected);
    }

    #[test]
    fn too_short_array() {
        let json = json!([200]);
        let failed_room_state = deserialize::<RoomState, _>(json);
        assert_eq!(
            failed_room_state.unwrap_err().to_string(),
            "invalid length 1, expected a two-element list in the response"
        );
    }

    #[test]
    fn not_an_array() {
        let json = json!({
            "origin": "matrix.org",
            "auth_chain": [],
            "state": []
        });
        let failed_room_state = deserialize::<RoomState, _>(json);

        assert_eq!(
            failed_room_state.unwrap_err().to_string(),
            "invalid type: map, expected a PDU wrapped in an array.",
        );
    }

    #[test]
    fn too_long_array() {
        let json = json!([200, { "origin": "", "auth_chain": [], "state": [] }, 200]);
        let RoomState { origin, auth_chain, state } = deserialize(json).unwrap();
        assert_eq!(origin, "");
        assert_matches!(auth_chain.as_slice(), []);
        assert_matches!(state.as_slice(), []);
    }
}
<|endoftext|>


<commit_before>use crate::{
    DynamicFlags1, DynamicInfo, ElfLoader, ElfLoaderErr, LoadableHeaders, RelocationEntry,
    RelocationType,
};
use core::fmt;
use log::*;
use xmas_elf::dynamic::Tag;
use xmas_elf::program::ProgramHeader::{self, Ph32, Ph64};
use xmas_elf::program::{ProgramIter, SegmentData, Type};
use xmas_elf::sections::SectionData;
pub use xmas_elf::symbol_table::{Entry, Entry64};
use xmas_elf::ElfFile;
use xmas_elf::*;

/// Abstract representation of a loadable ELF binary.
pub struct ElfBinary<'s> {
    /// The ELF file in question.
    pub file: ElfFile<'s>,
    /// Parsed information from the .dynamic section (if the binary has it).
    pub dynamic: Option<DynamicInfo>,
}

impl<'s> fmt::Debug for ElfBinary<'s> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ElfBinary{{ [")?;
        for p in self.program_headers() {
            write!(f, " pheader = {}", p)?;
        }
        write!(f, "] }}")
    }
}

impl<'s> ElfBinary<'s> {
    /// Create a new ElfBinary.
    pub fn new(region: &'s [u8]) -> Result<ElfBinary<'s>, ElfLoaderErr> {
        let file = ElfFile::new(region)?;

        // Parse relevant parts out of the the .dynamic section
        let mut dynamic = None;
        for p in file.program_iter() {
            let typ = match p {
                Ph64(header) => header.get_type()?,
                Ph32(header) => header.get_type()?,
            };

            if typ == Type::Dynamic {
                dynamic = ElfBinary::parse_dynamic(&file, &p)?;
                break;
            }
        }

        Ok(ElfBinary { file, dynamic })
    }

    /// Returns true if the binary is compiled as position independent code or false otherwise.
    ///
    /// For the binary to be PIE it needs to have a .dynamic section with PIE set in the flags1
    /// field.
    pub fn is_pie(&self) -> bool {
        self.dynamic.as_ref().map_or(false, |d: &DynamicInfo| {
            d.flags1.contains(DynamicFlags1::PIE)
        })
    }

    /// Returns the dynamic loader if present.
    ///
    /// readelf -x .interp <binary>
    ///
    /// For a statically compiled binary this will return None
    pub fn interpreter(&'s self) -> Option<&'s str> {
        let section = self.file.find_section_by_name(".interp");
        section.and_then(|interp_section| {
            let data = interp_section.get_data(&self.file).ok()?;
            let cstr = match data {
                SectionData::Undefined(val) => val,
                _ => return None,
            };

            // Validate there is room for a null terminator
            if cstr.len() < 2 {
                return None;
            }

            // Ensure it is a valid utf8 string
            core::str::from_utf8(&cstr[..cstr.len() - 1]).ok()
        })
    }

    /// Returns the target architecture
    pub fn get_arch(&self) -> header::Machine {
        self.file.header.pt2.machine().as_machine()
    }

    /// Return the entry point of the ELF file.
    ///
    /// Note this may be zero in case of position independent executables.
    pub fn entry_point(&self) -> u64 {
        self.file.header.pt2.entry_point()
    }

    /// Create a slice of the program headers.
    pub fn program_headers(&self) -> ProgramIter {
        self.file.program_iter()
    }

    /// Get the name of the sectione
    pub fn symbol_name(&self, symbol: &'s dyn Entry) -> &'s str {
        symbol.get_name(&self.file).unwrap_or("unknown")
    }

    /// Enumerate all the symbols in the file
    pub fn for_each_symbol<F: FnMut(&'s dyn Entry)>(
        &self,
        mut func: F,
    ) -> Result<(), ElfLoaderErr> {
        let symbol_section = self
            .file
            .find_section_by_name(".symtab")
            .ok_or(ElfLoaderErr::SymbolTableNotFound)?;
        let symbol_table = symbol_section.get_data(&self.file)?;
        match symbol_table {
            SectionData::SymbolTable32(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            SectionData::SymbolTable64(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            _ => Err(ElfLoaderErr::SymbolTableNotFound),
        }
    }

    /// Can we load this binary on our platform?
    fn is_loadable(&self) -> Result<(), ElfLoaderErr> {
        let header = self.file.header;
        let typ = header.pt2.type_().as_type();

        if header.pt1.version() != header::Version::Current {
            Err(ElfLoaderErr::UnsupportedElfVersion)
        } else if header.pt1.data() != header::Data::LittleEndian {
            Err(ElfLoaderErr::UnsupportedEndianness)
        } else if !(header.pt1.os_abi() == header::OsAbi::SystemV
            || header.pt1.os_abi() == header::OsAbi::Linux)
        {
            Err(ElfLoaderErr::UnsupportedAbi)
        } else if !(typ == header::Type::Executable || typ == header::Type::SharedObject) {
            error!("Invalid ELF type {:?}", typ);
            Err(ElfLoaderErr::UnsupportedElfType)
        } else {
            Ok(())
        }
    }

    /// Process the relocation entries for the ELF file.
    ///
    /// Issues call to `loader.relocate` and passes the relocation entry.
    fn maybe_relocate(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        // Relocation types are architecture specific
        let arch = self.get_arch();

        // It's easier to just locate the section by name, either:
        // - .rela.dyn
        // - .rel.dyn
        let relocation_section = self
            .file
            .find_section_by_name(".rela.dyn")
            .or_else(|| self.file.find_section_by_name(".rel.dyn"));

        // Helper macro to call loader.relocate() on all entries
        macro_rules! iter_entries_and_relocate {
            ($rela_entries:expr, $create_addend:ident) => {
                for entry in $rela_entries {
                    loader.relocate(RelocationEntry {
                        rtype: RelocationType::from(arch, entry.get_type() as u32)?,
                        offset: entry.get_offset() as u64,
                        index: entry.get_symbol_table_index(),
                        addend: $create_addend!(entry),
                    })?;
                }
            };
        }

        // Construct from Rel<T> entries. Does not contain an addend.
        macro_rules! rel_entry {
            ($entry:ident) => {
                None
            };
        }

        // Construct from Rela<T> entries. Contains an addend.
        macro_rules! rela_entry {
            ($entry:ident) => {
                Some($entry.get_addend() as u64)
            };
        }

        // If either section exists apply the relocations
        relocation_section.map_or(Ok(()), |rela_section_dyn| {
            let data = rela_section_dyn.get_data(&self.file)?;
            match data {
                SectionData::Rel32(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela32(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                SectionData::Rel64(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela64(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                _ => return Err(ElfLoaderErr::UnsupportedSectionData),
            }
            Ok(())
        })
    }

    /// Processes a dynamic header section.
    ///
    /// This section contains mostly entry points to other section headers (like relocation).
    /// At the moment this just does sanity checking for relocation later.
    ///
    /// A human readable version of the dynamic section is best obtained with `readelf -d <binary>`.
    fn parse_dynamic<'a>(
        file: &ElfFile,
        dynamic_header: &'a ProgramHeader<'a>,
    ) -> Result<Option<DynamicInfo>, ElfLoaderErr> {
        trace!("load dynamic segement {:?}", dynamic_header);

        // Walk through the dynamic program header and find the rela and sym_tab section offsets:
        let segment = dynamic_header.get_data(file)?;

        // Init result
        let mut info = DynamicInfo {
            flags1: Default::default(),
            rela: 0,
            rela_size: 0,
        };

        // Each entry/section is parsed for the same information currently
        macro_rules! parse_entry_tags {
            ($info:ident, $entry:ident, $tag:ident) => {
                match $tag {
                    // Trace required libs
                    Tag::Needed => {
                        trace!(
                            "Required library {:?}",
                            file.get_dyn_string($entry.get_val()? as _)
                        )
                    }

                    // Rel<T>
                    Tag::Rel => $info.rela = $entry.get_ptr()?.into(),
                    Tag::RelSize => $info.rela_size = $entry.get_val()?.into(),

                    // Rela<T>
                    Tag::Rela => $info.rela = $entry.get_ptr()?.into(),
                    Tag::RelaSize => $info.rela_size = $entry.get_val()?.into(),
                    Tag::Flags1 => {
                        $info.flags1 =
                            unsafe { DynamicFlags1::from_bits_unchecked($entry.get_val()? as _) };
                    }
                    _ => trace!("unsupported {:?}", $entry),
                }
            };
        }

        // Helper macro to iterate all entries
        macro_rules! iter_entries_and_parse {
            ($info:ident, $dyn_entries:expr) => {
                for dyn_entry in $dyn_entries {
                    let tag = dyn_entry.get_tag()?;
                    parse_entry_tags!($info, dyn_entry, tag);
                }
            };
        }

        match segment {
            SegmentData::Dynamic32(dyn_entries) => {
                iter_entries_and_parse!(info, dyn_entries);
            }
            SegmentData::Dynamic64(dyn_entries) => {
                iter_entries_and_parse!(info, dyn_entries);
            }
            _ => {
                return Err(ElfLoaderErr::UnsupportedSectionData);
            }
        };

        trace!(
            "rela size {:?} rela off {:?} flags1 {:?}",
            info.rela_size,
            info.rela,
            info.flags1
        );

        Ok(Some(info))
    }

    /// Processing the program headers and issue commands to loader.
    ///
    /// Will tell loader to create space in the address space / region where the
    /// header is supposed to go, then copy it there, and finally relocate it.
    pub fn load(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        self.is_loadable()?;

        loader.allocate(self.iter_loadable_headers())?;

        // Load all headers
        for header in self.file.program_iter() {
            let raw = match header {
                Ph32(inner) => inner.raw_data(&self.file),
                Ph64(inner) => inner.raw_data(&self.file),
            };
            let typ = header.get_type()?;
            match typ {
                Type::Load => {
                    loader.load(header.flags(), header.virtual_addr(), raw)?;
                }
                Type::Tls => {
                    loader.tls(
                        header.virtual_addr(),
                        header.file_size(),
                        header.mem_size(),
                        header.align(),
                    )?;
                }
                _ => {} // skip for now
            }
        }

        // Relocate headers
        self.maybe_relocate(loader)?;

        // Process .data.rel.ro
        for header in self.file.program_iter() {
            if header.get_type()? == Type::GnuRelro {
                loader.make_readonly(header.virtual_addr(), header.mem_size() as usize)?
            }
        }

        Ok(())
    }

    fn iter_loadable_headers(&self) -> LoadableHeaders {
        // Trying to determine loadeable headers
        fn select_load(pheader: &ProgramHeader) -> bool {
            match pheader {
                Ph32(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
                Ph64(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
            }
        }

        // Create an iterator (well filter really) that has all loadeable
        // headers and pass it to the loader
        // TODO: This is pretty ugly, maybe we can do something with impl Trait?
        // https://stackoverflow.com/questions/27535289/what-is-the-correct-way-to-return-an-iterator-or-any-other-trait
        self.file.program_iter().filter(select_load)
    }
}
<commit_msg>Ignore null program headers<commit_after>use crate::{
    DynamicFlags1, DynamicInfo, ElfLoader, ElfLoaderErr, LoadableHeaders, RelocationEntry,
    RelocationType,
};
use core::fmt;
use log::*;
use xmas_elf::dynamic::Tag;
use xmas_elf::program::ProgramHeader::{self, Ph32, Ph64};
use xmas_elf::program::{ProgramIter, SegmentData, Type};
use xmas_elf::sections::SectionData;
pub use xmas_elf::symbol_table::{Entry, Entry64};
use xmas_elf::ElfFile;
use xmas_elf::*;

/// Abstract representation of a loadable ELF binary.
pub struct ElfBinary<'s> {
    /// The ELF file in question.
    pub file: ElfFile<'s>,
    /// Parsed information from the .dynamic section (if the binary has it).
    pub dynamic: Option<DynamicInfo>,
}

impl<'s> fmt::Debug for ElfBinary<'s> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ElfBinary{{ [")?;
        for p in self.program_headers() {
            write!(f, " pheader = {}", p)?;
        }
        write!(f, "] }}")
    }
}

impl<'s> ElfBinary<'s> {
    /// Create a new ElfBinary.
    pub fn new(region: &'s [u8]) -> Result<ElfBinary<'s>, ElfLoaderErr> {
        let file = ElfFile::new(region)?;

        // Parse relevant parts out of the the .dynamic section
        let mut dynamic = None;
        for p in file.program_iter() {
            let typ = match p {
                Ph64(header) => header.get_type()?,
                Ph32(header) => header.get_type()?,
            };

            if typ == Type::Dynamic {
                dynamic = ElfBinary::parse_dynamic(&file, &p)?;
                break;
            }
        }

        Ok(ElfBinary { file, dynamic })
    }

    /// Returns true if the binary is compiled as position independent code or false otherwise.
    ///
    /// For the binary to be PIE it needs to have a .dynamic section with PIE set in the flags1
    /// field.
    pub fn is_pie(&self) -> bool {
        self.dynamic.as_ref().map_or(false, |d: &DynamicInfo| {
            d.flags1.contains(DynamicFlags1::PIE)
        })
    }

    /// Returns the dynamic loader if present.
    ///
    /// readelf -x .interp <binary>
    ///
    /// For a statically compiled binary this will return None
    pub fn interpreter(&'s self) -> Option<&'s str> {
        let section = self.file.find_section_by_name(".interp");
        section.and_then(|interp_section| {
            let data = interp_section.get_data(&self.file).ok()?;
            let cstr = match data {
                SectionData::Undefined(val) => val,
                _ => return None,
            };

            // Validate there is room for a null terminator
            if cstr.len() < 2 {
                return None;
            }

            // Ensure it is a valid utf8 string
            core::str::from_utf8(&cstr[..cstr.len() - 1]).ok()
        })
    }

    /// Returns the target architecture
    pub fn get_arch(&self) -> header::Machine {
        self.file.header.pt2.machine().as_machine()
    }

    /// Return the entry point of the ELF file.
    ///
    /// Note this may be zero in case of position independent executables.
    pub fn entry_point(&self) -> u64 {
        self.file.header.pt2.entry_point()
    }

    /// Create a slice of the program headers.
    pub fn program_headers(&self) -> ProgramIter {
        self.file.program_iter()
    }

    /// Get the name of the sectione
    pub fn symbol_name(&self, symbol: &'s dyn Entry) -> &'s str {
        symbol.get_name(&self.file).unwrap_or("unknown")
    }

    /// Enumerate all the symbols in the file
    pub fn for_each_symbol<F: FnMut(&'s dyn Entry)>(
        &self,
        mut func: F,
    ) -> Result<(), ElfLoaderErr> {
        let symbol_section = self
            .file
            .find_section_by_name(".symtab")
            .ok_or(ElfLoaderErr::SymbolTableNotFound)?;
        let symbol_table = symbol_section.get_data(&self.file)?;
        match symbol_table {
            SectionData::SymbolTable32(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            SectionData::SymbolTable64(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            _ => Err(ElfLoaderErr::SymbolTableNotFound),
        }
    }

    /// Can we load this binary on our platform?
    fn is_loadable(&self) -> Result<(), ElfLoaderErr> {
        let header = self.file.header;
        let typ = header.pt2.type_().as_type();

        if header.pt1.version() != header::Version::Current {
            Err(ElfLoaderErr::UnsupportedElfVersion)
        } else if header.pt1.data() != header::Data::LittleEndian {
            Err(ElfLoaderErr::UnsupportedEndianness)
        } else if !(header.pt1.os_abi() == header::OsAbi::SystemV
            || header.pt1.os_abi() == header::OsAbi::Linux)
        {
            Err(ElfLoaderErr::UnsupportedAbi)
        } else if !(typ == header::Type::Executable || typ == header::Type::SharedObject) {
            error!("Invalid ELF type {:?}", typ);
            Err(ElfLoaderErr::UnsupportedElfType)
        } else {
            Ok(())
        }
    }

    /// Process the relocation entries for the ELF file.
    ///
    /// Issues call to `loader.relocate` and passes the relocation entry.
    fn maybe_relocate(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        // Relocation types are architecture specific
        let arch = self.get_arch();

        // It's easier to just locate the section by name, either:
        // - .rela.dyn
        // - .rel.dyn
        let relocation_section = self
            .file
            .find_section_by_name(".rela.dyn")
            .or_else(|| self.file.find_section_by_name(".rel.dyn"));

        // Helper macro to call loader.relocate() on all entries
        macro_rules! iter_entries_and_relocate {
            ($rela_entries:expr, $create_addend:ident) => {
                for entry in $rela_entries {
                    loader.relocate(RelocationEntry {
                        rtype: RelocationType::from(arch, entry.get_type() as u32)?,
                        offset: entry.get_offset() as u64,
                        index: entry.get_symbol_table_index(),
                        addend: $create_addend!(entry),
                    })?;
                }
            };
        }

        // Construct from Rel<T> entries. Does not contain an addend.
        macro_rules! rel_entry {
            ($entry:ident) => {
                None
            };
        }

        // Construct from Rela<T> entries. Contains an addend.
        macro_rules! rela_entry {
            ($entry:ident) => {
                Some($entry.get_addend() as u64)
            };
        }

        // If either section exists apply the relocations
        relocation_section.map_or(Ok(()), |rela_section_dyn| {
            let data = rela_section_dyn.get_data(&self.file)?;
            match data {
                SectionData::Rel32(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela32(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                SectionData::Rel64(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela64(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                _ => return Err(ElfLoaderErr::UnsupportedSectionData),
            }
            Ok(())
        })
    }

    /// Processes a dynamic header section.
    ///
    /// This section contains mostly entry points to other section headers (like relocation).
    /// At the moment this just does sanity checking for relocation later.
    ///
    /// A human readable version of the dynamic section is best obtained with `readelf -d <binary>`.
    fn parse_dynamic<'a>(
        file: &ElfFile,
        dynamic_header: &'a ProgramHeader<'a>,
    ) -> Result<Option<DynamicInfo>, ElfLoaderErr> {
        trace!("load dynamic segement {:?}", dynamic_header);

        // Walk through the dynamic program header and find the rela and sym_tab section offsets:
        let segment = dynamic_header.get_data(file)?;

        // Init result
        let mut info = DynamicInfo {
            flags1: Default::default(),
            rela: 0,
            rela_size: 0,
        };

        // Each entry/section is parsed for the same information currently
        macro_rules! parse_entry_tags {
            ($info:ident, $entry:ident, $tag:ident) => {
                match $tag {
                    // Trace required libs
                    Tag::Needed => {
                        trace!(
                            "Required library {:?}",
                            file.get_dyn_string($entry.get_val()? as _)
                        )
                    }

                    // Rel<T>
                    Tag::Rel => $info.rela = $entry.get_ptr()?.into(),
                    Tag::RelSize => $info.rela_size = $entry.get_val()?.into(),

                    // Rela<T>
                    Tag::Rela => $info.rela = $entry.get_ptr()?.into(),
                    Tag::RelaSize => $info.rela_size = $entry.get_val()?.into(),
                    Tag::Flags1 => {
                        $info.flags1 =
                            unsafe { DynamicFlags1::from_bits_unchecked($entry.get_val()? as _) };
                    }
                    _ => trace!("unsupported {:?}", $entry),
                }
            };
        }

        // Helper macro to iterate all entries
        macro_rules! iter_entries_and_parse {
            ($info:ident, $dyn_entries:expr) => {
                for dyn_entry in $dyn_entries {
                    let tag = dyn_entry.get_tag()?;
                    parse_entry_tags!($info, dyn_entry, tag);
                }
            };
        }

        match segment {
            SegmentData::Dynamic32(dyn_entries) => {
                iter_entries_and_parse!(info, dyn_entries);
            }
            SegmentData::Dynamic64(dyn_entries) => {
                iter_entries_and_parse!(info, dyn_entries);
            }
            _ => {
                return Err(ElfLoaderErr::UnsupportedSectionData);
            }
        };

        trace!(
            "rela size {:?} rela off {:?} flags1 {:?}",
            info.rela_size,
            info.rela,
            info.flags1
        );

        Ok(Some(info))
    }

    /// Processing the program headers and issue commands to loader.
    ///
    /// Will tell loader to create space in the address space / region where the
    /// header is supposed to go, then copy it there, and finally relocate it.
    pub fn load(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        self.is_loadable()?;

        loader.allocate(self.iter_loadable_headers())?;

        // Load all headers
        for header in self.file.program_iter() {
            if header.get_type() == Ok(Type::Null) {
                continue;
            }

            let raw = match header {
                Ph32(inner) => inner.raw_data(&self.file),
                Ph64(inner) => inner.raw_data(&self.file),
            };
            let typ = header.get_type()?;
            match typ {
                Type::Load => {
                    loader.load(header.flags(), header.virtual_addr(), raw)?;
                }
                Type::Tls => {
                    loader.tls(
                        header.virtual_addr(),
                        header.file_size(),
                        header.mem_size(),
                        header.align(),
                    )?;
                }
                _ => {} // skip for now
            }
        }

        // Relocate headers
        self.maybe_relocate(loader)?;

        // Process .data.rel.ro
        for header in self.file.program_iter() {
            if header.get_type()? == Type::GnuRelro {
                loader.make_readonly(header.virtual_addr(), header.mem_size() as usize)?
            }
        }

        Ok(())
    }

    fn iter_loadable_headers(&self) -> LoadableHeaders {
        // Trying to determine loadeable headers
        fn select_load(pheader: &ProgramHeader) -> bool {
            match pheader {
                Ph32(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
                Ph64(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
            }
        }

        // Create an iterator (well filter really) that has all loadeable
        // headers and pass it to the loader
        // TODO: This is pretty ugly, maybe we can do something with impl Trait?
        // https://stackoverflow.com/questions/27535289/what-is-the-correct-way-to-return-an-iterator-or-any-other-trait
        self.file.program_iter().filter(select_load)
    }
}
<|endoftext|>