<commit_before>/// The net will be represented by a swarm as in libp2p
/// https://docs.rs/libp2p/latest/libp2p/swarm/index.html.
///
/// Because swarm is the manager a state machine as before could be replaced, also the working
/// Mdns Server/Client can just be transparently added used in the behavior of this network.
/// As transport layer, an experimental but prospering protocol called "noise protocol" will
/// be used.
/// The network communication is basically an actor system just as used here in the webui
/// with actix actor, which btw could maybe also replaced by the websocket protocol from
/// libp2p, but for now it will stay in a nice, small http server.
///
/// The noise protocol to be used
/// (http://noiseprotocol.org/)
///
use super::{
    super::net::peer_representation::{self, PeerRepresentation},
    sm_behaviour::{SMBehaviour, SMOutEvents},
    IPC,
};

use async_std::io;
use bincode;
use bincode::deserialize;
use libp2p::{
    kad::{
        record,
        store::{MemoryStore, RecordStore},
        Kademlia, KademliaEvent, PeerRecord, PutRecordOk, QueryResult, Quorum, Record,
    },
    mdns::{Mdns, MdnsEvent},
    pnet::{PnetConfig, PreSharedKey},
    swarm::NetworkBehaviourEventProcess,
    yamux::Config as YamuxConfig,
    NetworkBehaviour,
};
use libp2p_core::{
    either::EitherTransport, identity, transport::upgrade, PeerId, StreamMuxer, Transport,
};
use libp2p_noise::{Keypair, NoiseConfig, X25519Spec};
use libp2p_tcp::TcpConfig;
use std::{error::Error, time::Duration};

#[allow(non_camel_case_types)]
#[derive(Serialize, Deserialize, Clone, Debug)]
enum MkadKeys {
    KeyForPeerFinished(PeerRepresentation),
}

/// The swarm injected behavior is the key element for the whole communication
/// See https://docs.rs/libp2p/0.21.1/libp2p/swarm/trait.NetworkBehaviour.html for more
#[derive(NetworkBehaviour)]
pub struct AdbfBehavior {
    pub kademlia: Kademlia<MemoryStore>,
    pub mdns: Mdns,
    pub sm_behaviour: SMBehaviour,
}
impl NetworkBehaviourEventProcess<MdnsEvent> for AdbfBehavior {
    // Called when `mdns` produces an event.
    fn inject_event(&mut self, event: MdnsEvent) {
        match event {
            MdnsEvent::Discovered(list) => {
                // most probably not the same PeerId ;-) but since it is multiaddress
                // its almost impossible to have 4 times the same random multiaddress
                let mut old_display_peer = PeerId::random();
                for (peer_id, multiaddr) in list {
                    self.sm_behaviour.mdns_new_peer(&peer_id, &multiaddr);
                    self.kademlia.add_address(&peer_id, multiaddr);
                    if old_display_peer != peer_id {
                        self.check_peer_actions(&peer_id);
                        old_display_peer = peer_id.clone();
                    }
                }
            }
            MdnsEvent::Expired(expired_addresses) => {
                for (peer_id, multi_addr) in expired_addresses {
                    self.kademlia.remove_address(&peer_id, &multi_addr);
                    self.sm_behaviour.mdns_remove(&peer_id);
                }
            }
        }
    }
}
impl NetworkBehaviourEventProcess<KademliaEvent> for AdbfBehavior {
    // Called when `kademlia` produces an event.
    fn inject_event(&mut self, message: KademliaEvent) {
        match message {
            KademliaEvent::QueryResult { result, .. } => match result {
                QueryResult::GetRecord(get_record) => match get_record {
                    Ok(ok) => {
                        for PeerRecord {
                            record: Record { key, value, .. },
                            ..
                        } in ok.records
                        {
                            self.retrieve_record(key, value);
                        }
                    }
                    Err(err) => {
                        error!("Failed to get record: {:?}", err);
                    }
                },
                QueryResult::PutRecord(put_record) => match put_record {
                    Ok(PutRecordOk { key }) => {
                        let raw_key = Self::key_reader(&key);
                        match raw_key {
                            Ok(deserialized) => match deserialized {
                                MkadKeys::KeyForPeerFinished(peer_hash) => info!(
                                    "Successfully put record key KeyForPeerFinished for peer {:?}",
                                    peer_representation::peer_hash_to_string(&peer_hash)
                                ),
                            },
                            Err(_) => {
                                error!("key could not be verified!");
                            }
                        }
                    }
                    Err(err) => {
                        error!("Failed to put record: {:?}", err);
                    }
                },
                _ => trace!("other kademlie query results arrived?!"),
            },
            _ => (), // trace!("kademlie routing events occured!"),
        }
    }
}

impl NetworkBehaviourEventProcess<SMOutEvents> for AdbfBehavior {
    // Called when SM produces an event.
    fn inject_event(&mut self, event: SMOutEvents) {
        // send whole event
        match event {
            SMOutEvents::ForwardSM(sm_event) => {
                // there is none yet
            }
            SMOutEvents::ForwardIPC(ipc_event) => {
                // the key is to avoid duplicate, so the key
                // is a hash of the message itself
                match ipc_event {
                    IPC::DoneSearching(nr) => {
                        info!("forward ipc {:?}", ipc_event);
                        let own_peer =
                            peer_representation::peer_to_hash(&self.sm_behaviour.own_peer());

                        let bin_key = Self::key_writer(MkadKeys::KeyForPeerFinished(own_peer));

                        // try to read old value
                        let mut value_to_send = nr;
                        if let Some(value_ipc) = self.kademlia.store_mut().get(&bin_key) {
                            let found: u32 = bincode::deserialize(value_ipc.value.as_ref())
                                .unwrap_or_else(|_| {
                                    error!("value in store is not what expected!");
                                    0
                                });
                            value_to_send += found;
                        } else {
                            trace!(
                                "this key was not yet set in the kademlia store with value {}!",
                                nr
                            );
                        }
                        // for provider later
                        let provider_key = bin_key.clone();

                        let bin_message =
                            bincode::serialize(&IPC::DoneSearching(value_to_send)).unwrap();

                        let record = Record {
                            key: bin_key,
                            value: bin_message,
                            publisher: None,
                            expires: None,
                        };

                        // write out
                        self.kademlia
                            .put_record(record, Quorum::One)
                            .expect("Failed to store record locally.");
                        // and be the provider for that key
                        match self.kademlia.start_providing(provider_key) {
                            Ok(query_id) => (), //trace!("providing worked"),
                            Err(e) => error!("providing failed"),
                        }
                    }
                }
            }
        }
    }
}

pub fn build_noise_transport(
    key_pair: &identity::Keypair,
    psk: Option<PreSharedKey>,
) -> impl Transport<
    Output = (
        PeerId,
        impl StreamMuxer<
                OutboundSubstream = impl Send,
                Substream = impl Send,
                Error = impl Into<io::Error>,
            > + Send
            + Sync,
    ),
    Error = impl Error + Send,
    Listener = impl Send,
    Dial = impl Send,
    ListenerUpgrade = impl Send,
> + Clone {
    let dh_keys = Keypair::<X25519Spec>::new()
        .into_authentic(&key_pair)
        .unwrap();
    let noise_config = NoiseConfig::xx(dh_keys).into_authenticated();
    let yamux_config = YamuxConfig::default();

    let base_transport = TcpConfig::new().nodelay(true);
    let maybe_encrypted = match psk {
        Some(psk) => EitherTransport::Left(
            base_transport.and_then(move |socket, _| PnetConfig::new(psk).handshake(socket)),
        ),
        None => EitherTransport::Right(base_transport),
    };
    maybe_encrypted
        .upgrade(upgrade::Version::V1)
        .authenticate(noise_config)
        .multiplex(yamux_config)
        .timeout(Duration::from_secs(20))
}

impl AdbfBehavior {
    fn retrieve_record(&mut self, key: record::Key, value: Vec<u8>) {
        warn!("..............................................");
        if let Ok(fits_mkad_keys) = Self::key_reader(&key) {
            match fits_mkad_keys {
                MkadKeys::KeyForPeerFinished(peer_hash) => {
                    //
                    info!("key for peer finished: {}!", peer_hash);
                }
            }
        } else {
            error!("unknown MkadKeys format");
        }
    }

    fn key_writer(internal_key: MkadKeys) -> record::Key {
        let bin_key = bincode::serialize(&internal_key).unwrap();
        record::Key::new(&bin_key)
    }
    fn key_reader(
        key_record: &record::Key,
    ) -> Result<MkadKeys, std::boxed::Box<bincode::ErrorKind>> {
        bincode::deserialize(key_record.as_ref())
    }

    fn get_key_finished(&mut self, key: MkadKeys) -> Result<u32, ()> {
        let serialized_key = Self::key_writer(key);
        match self.kademlia.store_mut().get(&serialized_key) {
            Some(good_query) => {
                let found: u32 =
                    bincode::deserialize(good_query.value.as_ref()).unwrap_or_else(|_| {
                        error!("value in store is not what expected!");
                        0
                    });
                Ok(found)
            }
            None => Err(()),
        }
    }

    fn check_if_peer_finished(&mut self, peer_id: &PeerId) -> Result<u32, ()> {
        let peer_hash = peer_representation::peer_to_hash(peer_id);
        let query_key = MkadKeys::KeyForPeerFinished(peer_hash);
        self.get_key_finished(query_key)
    }

    fn add_myself_to_peers_done(&mut self, peer_id: &PeerId) {
        // see if I am already in there
        let peer_hash = peer_representation::peer_to_hash(peer_id);
        let query_key = Self::key_writer(MkadKeys::KeyForPeerFinished(peer_hash));
        let kad_record = self.kademlia.get_record(&query_key, Quorum::Majority);
    }

    fn check_peer_actions(&mut self, peer_id: &PeerId) {
        if *peer_id == self.sm_behaviour.own_peer() {
            trace!("own instance finished ... not interesting");
        } else {
            let display_peer = peer_representation::peer_to_hash_string(&peer_id);
            if let Ok(count) = self.check_if_peer_finished(&peer_id) {
                trace!("yes, peer {} finished already", display_peer);
                self.sm_behaviour.update_peer_data(&peer_id, count);
            }
        }
    }
}
<commit_msg>[net] found bug in unnecessary wrapping of u32 value<commit_after>/// The net will be represented by a swarm as in libp2p
/// https://docs.rs/libp2p/latest/libp2p/swarm/index.html.
///
/// Because swarm is the manager a state machine as before could be replaced, also the working
/// Mdns Server/Client can just be transparently added used in the behavior of this network.
/// As transport layer, an experimental but prospering protocol called "noise protocol" will
/// be used.
/// The network communication is basically an actor system just as used here in the webui
/// with actix actor, which btw could maybe also replaced by the websocket protocol from
/// libp2p, but for now it will stay in a nice, small http server.
///
/// The noise protocol to be used
/// (http://noiseprotocol.org/)
///
use super::{
    super::net::peer_representation::{self, PeerRepresentation},
    sm_behaviour::{SMBehaviour, SMOutEvents},
    IPC,
};

use async_std::io;
use bincode;
use bincode::deserialize;
use libp2p::{
    kad::{
        record,
        store::{MemoryStore, RecordStore},
        Kademlia, KademliaEvent, PeerRecord, PutRecordOk, QueryResult, Quorum, Record,
    },
    mdns::{Mdns, MdnsEvent},
    pnet::{PnetConfig, PreSharedKey},
    swarm::NetworkBehaviourEventProcess,
    yamux::Config as YamuxConfig,
    NetworkBehaviour,
};
use libp2p_core::{
    either::EitherTransport, identity, transport::upgrade, PeerId, StreamMuxer, Transport,
};
use libp2p_noise::{Keypair, NoiseConfig, X25519Spec};
use libp2p_tcp::TcpConfig;
use std::{error::Error, time::Duration};

#[allow(non_camel_case_types)]
#[derive(Serialize, Deserialize, Clone, Debug)]
enum MkadKeys {
    KeyForPeerFinished(PeerRepresentation),
}

/// The swarm injected behavior is the key element for the whole communication
/// See https://docs.rs/libp2p/0.21.1/libp2p/swarm/trait.NetworkBehaviour.html for more
#[derive(NetworkBehaviour)]
pub struct AdbfBehavior {
    pub kademlia: Kademlia<MemoryStore>,
    pub mdns: Mdns,
    pub sm_behaviour: SMBehaviour,
}
impl NetworkBehaviourEventProcess<MdnsEvent> for AdbfBehavior {
    // Called when `mdns` produces an event.
    fn inject_event(&mut self, event: MdnsEvent) {
        match event {
            MdnsEvent::Discovered(list) => {
                // most probably not the same PeerId ;-) but since it is multiaddress
                // its almost impossible to have 4 times the same random multiaddress
                let mut old_display_peer = PeerId::random();
                for (peer_id, multiaddr) in list {
                    self.sm_behaviour.mdns_new_peer(&peer_id, &multiaddr);
                    self.kademlia.add_address(&peer_id, multiaddr);
                    if old_display_peer != peer_id {
                        self.check_peer_actions(&peer_id);
                        old_display_peer = peer_id.clone();
                    }
                }
            }
            MdnsEvent::Expired(expired_addresses) => {
                for (peer_id, multi_addr) in expired_addresses {
                    self.kademlia.remove_address(&peer_id, &multi_addr);
                    self.sm_behaviour.mdns_remove(&peer_id);
                }
            }
        }
    }
}
impl NetworkBehaviourEventProcess<KademliaEvent> for AdbfBehavior {
    // Called when `kademlia` produces an event.
    fn inject_event(&mut self, message: KademliaEvent) {
        match message {
            KademliaEvent::QueryResult { result, .. } => match result {
                QueryResult::GetRecord(get_record) => match get_record {
                    Ok(ok) => {
                        for PeerRecord {
                            record: Record { key, value, .. },
                            ..
                        } in ok.records
                        {
                            self.retrieve_record(key, value);
                        }
                    }
                    Err(err) => {
                        error!("Failed to get record: {:?}", err);
                    }
                },
                QueryResult::PutRecord(put_record) => match put_record {
                    Ok(PutRecordOk { key }) => {
                        let raw_key = Self::key_reader(&key);
                        match raw_key {
                            Ok(deserialized) => match deserialized {
                                MkadKeys::KeyForPeerFinished(peer_hash) => info!(
                                    "Successfully put record key KeyForPeerFinished for peer {:?}",
                                    peer_representation::peer_hash_to_string(&peer_hash)
                                ),
                            },
                            Err(_) => {
                                error!("key could not be verified!");
                            }
                        }
                    }
                    Err(err) => {
                        error!("Failed to put record: {:?}", err);
                    }
                },
                _ => trace!("other kademlie query results arrived?!"),
            },
            _ => (), // trace!("kademlie routing events occured!"),
        }
    }
}

impl NetworkBehaviourEventProcess<SMOutEvents> for AdbfBehavior {
    // Called when SM produces an event.
    fn inject_event(&mut self, event: SMOutEvents) {
        // send whole event
        match event {
            SMOutEvents::ForwardSM(sm_event) => {
                // there is none yet
            }
            SMOutEvents::ForwardIPC(ipc_event) => {
                // the key is to avoid duplicate, so the key
                // is a hash of the message itself
                match ipc_event {
                    IPC::DoneSearching(nr) => {
                        info!("forward ipc {:?}", ipc_event);
                        let own_peer =
                            peer_representation::peer_to_hash(&self.sm_behaviour.own_peer());

                        let bin_key = Self::key_writer(MkadKeys::KeyForPeerFinished(own_peer));

                        // try to read old value
                        let mut value_to_send = nr;
                        if let Some(value_ipc) = self.kademlia.store_mut().get(&bin_key) {
                            let found: u32 = bincode::deserialize(value_ipc.value.as_ref())
                                .unwrap_or_else(|_| {
                                    error!("value in store is not what expected!");
                                    0
                                });
                            value_to_send += found;
                        } else {
                            trace!(
                                "this key was not yet set in the kademlia store with value {}!",
                                nr
                            );
                        }
                        let bin_message = bincode::serialize(&value_to_send).unwrap();

                        let record = Record {
                            key: bin_key,
                            value: bin_message,
                            publisher: None,
                            expires: None,
                        };

                        // write out
                        self.kademlia
                            .put_record(record, Quorum::One)
                            .expect("Failed to store record locally.");
                    }
                }
            }
        }
    }
}

pub fn build_noise_transport(
    key_pair: &identity::Keypair,
    psk: Option<PreSharedKey>,
) -> impl Transport<
    Output = (
        PeerId,
        impl StreamMuxer<
                OutboundSubstream = impl Send,
                Substream = impl Send,
                Error = impl Into<io::Error>,
            > + Send
            + Sync,
    ),
    Error = impl Error + Send,
    Listener = impl Send,
    Dial = impl Send,
    ListenerUpgrade = impl Send,
> + Clone {
    let dh_keys = Keypair::<X25519Spec>::new()
        .into_authentic(&key_pair)
        .unwrap();
    let noise_config = NoiseConfig::xx(dh_keys).into_authenticated();
    let yamux_config = YamuxConfig::default();

    let base_transport = TcpConfig::new().nodelay(true);
    let maybe_encrypted = match psk {
        Some(psk) => EitherTransport::Left(
            base_transport.and_then(move |socket, _| PnetConfig::new(psk).handshake(socket)),
        ),
        None => EitherTransport::Right(base_transport),
    };
    maybe_encrypted
        .upgrade(upgrade::Version::V1)
        .authenticate(noise_config)
        .multiplex(yamux_config)
        .timeout(Duration::from_secs(20))
}

impl AdbfBehavior {
    fn retrieve_record(&mut self, key: record::Key, value: Vec<u8>) {
        warn!("..............................................");
        if let Ok(fits_mkad_keys) = Self::key_reader(&key) {
            match fits_mkad_keys {
                MkadKeys::KeyForPeerFinished(peer_hash) => {
                    //
                    info!("key for peer finished: {}!", peer_hash);
                }
            }
        } else {
            error!("unknown MkadKeys format");
        }
    }

    fn key_writer(internal_key: MkadKeys) -> record::Key {
        let bin_key = bincode::serialize(&internal_key).unwrap();
        record::Key::new(&bin_key)
    }
    fn key_reader(
        key_record: &record::Key,
    ) -> Result<MkadKeys, std::boxed::Box<bincode::ErrorKind>> {
        bincode::deserialize(key_record.as_ref())
    }

    fn get_key_finished(&mut self, key: MkadKeys) -> Result<u32, ()> {
        let serialized_key = Self::key_writer(key);
        match self.kademlia.store_mut().get(&serialized_key) {
            Some(good_query) => {
                let found: u32 =
                    bincode::deserialize(good_query.value.as_ref()).unwrap_or_else(|_| {
                        error!("value in store is not what expected!");
                        0
                    });
                Ok(found)
            }
            None => Err(()),
        }
    }

    fn check_if_peer_finished(&mut self, peer_id: &PeerId) -> Result<u32, ()> {
        let peer_hash = peer_representation::peer_to_hash(peer_id);
        let query_key = MkadKeys::KeyForPeerFinished(peer_hash);
        self.get_key_finished(query_key)
    }

    fn add_myself_to_peers_done(&mut self, peer_id: &PeerId) {
        // see if I am already in there
        let peer_hash = peer_representation::peer_to_hash(peer_id);
        let query_key = Self::key_writer(MkadKeys::KeyForPeerFinished(peer_hash));
        let kad_record = self.kademlia.get_record(&query_key, Quorum::Majority);
    }

    fn check_peer_actions(&mut self, peer_id: &PeerId) {
        if *peer_id == self.sm_behaviour.own_peer() {
            trace!("own instance finished ... not interesting");
        } else {
            let display_peer = peer_representation::peer_to_hash_string(&peer_id);
            if let Ok(count) = self.check_if_peer_finished(&peer_id) {
                trace!("yes, peer {} finished already", display_peer);
                self.sm_behaviour.update_peer_data(&peer_id, count);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Write;

use crate::pdu::reader::PDU_HEADER_SIZE;

/// A P-Data value writer.
///
/// This exposes an API to iteratively construct and send Data messages
/// to another node.
/// Using this as a [standard writer](std::io::Write)
/// will automatically split the incoming bytes
/// into separate PDUs if they do not fit in a single one.
///
/// # Example
///
/// Use an association's `send_pdata` method
/// to create a new P-Data value writer.
///
/// ```no_run
/// # use std::io::Write;
/// # use dicom_ul::association::{ClientAssociationOptions, PDataWriter};
/// # use dicom_ul::pdu::{Pdu, PDataValue, PDataValueType};
/// # fn command_data() -> Vec<u8> { unimplemented!() }
/// # fn dicom_data() -> &'static [u8] { unimplemented!() }
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut association = ClientAssociationOptions::new()
///    .establish("129.168.0.5:104")?;
///
/// let presentation_context_id = association.presentation_contexts()[0].id;
///
/// // send a command first
/// association.send(&Pdu::PData {
///     data: vec![PDataValue {
///     presentation_context_id,
///     value_type: PDataValueType::Command,
///         is_last: true,
///         data: command_data(),
///     }],
/// });
///
/// // then send a DICOM object which may be split into multiple PDUs
/// let mut pdata = association.send_pdata(presentation_context_id);
/// pdata.write_all(dicom_data())?;
/// pdata.finish()?;
///
/// let pdu_ac = association.receive()?;
/// # Ok(())
/// # }
#[must_use]
pub struct PDataWriter<W: Write> {
    buffer: Vec<u8>,
    stream: W,
    presentation_context_id: u8,
    max_data_len: u32,
}

impl<W> PDataWriter<W>
where
    W: Write,
{
    /// Construct a new P-Data value writer.
    ///
    /// `max_pdu_length` is the maximum value of the PDU-length property.
    pub(crate) fn new(stream: W, presentation_context_id: u8, max_pdu_length: u32) -> Self {
        let max_data_length = calculate_max_data_len_single(max_pdu_length);
        PDataWriter {
            stream,
            presentation_context_id,
            max_data_len: max_data_length,
            buffer: Vec::with_capacity(max_data_length as usize),
        }
    }

    /// Declare to have finished sending P-Data fragments,
    /// thus emitting the last P-Data fragment PDU.
    ///
    /// This is also done automatically once the P-Data writer is dropped.
    pub fn finish(mut self) -> std::io::Result<()> {
        self.finish_impl()?;
        Ok(())
    }

    fn create_pdata_header(&self, data_len: u32, is_last: bool) -> [u8; 12] {
        let pdu_len = data_len + 4 + 2;
        let pdv_data_len = data_len + 2;
        let pdu_len_bytes = pdu_len.to_be_bytes();
        let data_len_bytes = pdv_data_len.to_be_bytes();
        [
            // PDU-type + reserved byte
            0x04,
            0x00,
            // full PDU length (minus PDU type and reserved byte)
            pdu_len_bytes[0],
            pdu_len_bytes[1],
            pdu_len_bytes[2],
            pdu_len_bytes[3],
            // presentation data length (data + 2 properties below)
            data_len_bytes[0],
            data_len_bytes[1],
            data_len_bytes[2],
            data_len_bytes[3],
            // presentation context id
            self.presentation_context_id,
            // message control header
            if is_last { 0x02 } else { 0x00 },
        ]
    }

    /// Send the header of a single P-Data PDU,
    /// containing a single data fragment.
    fn send_pdata_header(&mut self, data_len: u32, is_last: bool) -> std::io::Result<()> {
        self.stream
            .write_all(&self.create_pdata_header(data_len, is_last))
    }

    fn finish_impl(&mut self) -> std::io::Result<()> {
        self.dispatch_excess_data()?;
        if !self.buffer.is_empty() {
            // send last PDU
            self.send_pdata_header(self.buffer.len() as u32, true)?;
            self.stream.write_all(&self.buffer[..])?;
            // clear buffer so that subsequent calls to `finish_impl`
            // do not send any more PDUs
            self.buffer.clear();
        }
        Ok(())
    }

    /// Use the current state of the buffer to send new PDUs
    ///
    /// Pre-condition:
    /// buffer must have enough data for one full P-Data-tf PDU
    fn dispatch_pdus(&mut self) -> std::io::Result<()> {
        debug_assert!(self.buffer.len() >= self.max_data_len as usize);
        // send PDU now

        let mut acc_len = 0;
        for chunk in self.buffer.chunks(self.max_data_len as usize) {
            if chunk.len() < self.max_data_len as usize {
                break;
            }
            let header = self.create_pdata_header(self.max_data_len, false);
            self.stream.write_all(&header)?;
            self.stream.write_all(chunk)?;
            acc_len += chunk.len();
        }

        // shift the remaining contents to the beginning of the buffer
        // (guaranteed to be shorter than `self.max_data_len`)
        let (p1, p2) = (&mut self.buffer[..]).split_at_mut(acc_len);
        for (e1, e2) in std::iter::Iterator::zip(p1.iter_mut(), p2.iter()) {
            *e1 = *e2;
        }
        self.buffer.truncate(self.buffer.len() - acc_len as usize);

        Ok(())
    }

    fn dispatch_excess_data(&mut self) -> std::io::Result<()> {
        while self.buffer.len() > self.max_data_len as usize {
            self.dispatch_pdus()?;
        }
        Ok(())
    }
}

impl<W> Write for PDataWriter<W>
where
    W: Write,
{
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.buffer.extend(buf);
        self.dispatch_excess_data()?;
        Ok(buf.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        // do nothing
        Ok(())
    }
}

/// With the P-Data writer dropped,
/// this `Drop` implementation
/// will construct and emit the last P-Data fragment PDU
/// if there is any data left to send.
impl<W> Drop for PDataWriter<W>
where
    W: Write,
{
    fn drop(&mut self) {
        let _ = self.finish_impl();
    }
}

/// Determine the maximum length of actual PDV data
/// when encapsulated in a PDU with the given length property.
/// Does not account for the first 2 bytes (type + reserved).
#[inline]
fn calculate_max_data_len_single(pdu_len: u32) -> u32 {
    // data length: 4 bytes
    // control header: 2 bytes
    pdu_len - PDU_HEADER_SIZE
}

#[cfg(test)]
mod tests {
    use std::io::Write;

    use crate::pdu::reader::{read_pdu, MINIMUM_PDU_SIZE, PDU_HEADER_SIZE};
    use crate::pdu::PDataValueType;
    use crate::pdu::Pdu;

    use super::PDataWriter;

    #[test]
    fn test_write_pdata_and_finish() {
        let presentation_context_id = 12;

        let mut buf = Vec::new();
        {
            let mut writer = PDataWriter::new(&mut buf, presentation_context_id, MINIMUM_PDU_SIZE);
            writer.write_all(&(0..64).collect::<Vec<u8>>()).unwrap();
            writer.finish().unwrap();
        }

        let mut cursor = &buf[..];
        let same_pdu = read_pdu(&mut cursor, MINIMUM_PDU_SIZE, true).unwrap();

        // concatenate data chunks, compare with all data

        match same_pdu {
            Pdu::PData { data: data_1 } => {
                let data_1 = &data_1[0];

                // check that this PDU is consistent
                assert_eq!(data_1.value_type, PDataValueType::Data);
                assert_eq!(data_1.presentation_context_id, presentation_context_id);
                assert_eq!(data_1.data.len(), 64);
                assert_eq!(data_1.data, (0..64).collect::<Vec<u8>>());
            }
            pdu => panic!("Expected PData, got {:?}", pdu),
        }

        assert_eq!(cursor.len(), 0);
    }

    #[test]
    fn test_write_large_pdata_and_finish() {
        let presentation_context_id = 32;

        let my_data: Vec<_> = (0..9000).map(|x: u32| x as u8).collect();

        let mut buf = Vec::new();
        {
            let mut writer = PDataWriter::new(&mut buf, presentation_context_id, MINIMUM_PDU_SIZE);
            writer.write_all(&my_data).unwrap();
            writer.finish().unwrap();
        }

        let mut cursor = &buf[..];
        let pdu_1 = read_pdu(&mut cursor, MINIMUM_PDU_SIZE, true).unwrap();
        let pdu_2 = read_pdu(&mut cursor, MINIMUM_PDU_SIZE, true).unwrap();
        let pdu_3 = read_pdu(&mut cursor, MINIMUM_PDU_SIZE, true).unwrap();

        // concatenate data chunks, compare with all data

        match (pdu_1, pdu_2, pdu_3) {
            (
                Pdu::PData { data: data_1 },
                Pdu::PData { data: data_2 },
                Pdu::PData { data: data_3 },
            ) => {
                assert_eq!(data_1.len(), 1);
                let data_1 = &data_1[0];
                assert_eq!(data_2.len(), 1);
                let data_2 = &data_2[0];
                assert_eq!(data_3.len(), 1);
                let data_3 = &data_3[0];

                // check that these two PDUs are consistent
                assert_eq!(data_1.value_type, PDataValueType::Data);
                assert_eq!(data_2.value_type, PDataValueType::Data);
                assert_eq!(data_1.presentation_context_id, presentation_context_id);
                assert_eq!(data_2.presentation_context_id, presentation_context_id);

                // check expected lengths
                assert_eq!(
                    data_1.data.len(),
                    (MINIMUM_PDU_SIZE - PDU_HEADER_SIZE) as usize
                );
                assert_eq!(
                    data_2.data.len(),
                    (MINIMUM_PDU_SIZE - PDU_HEADER_SIZE) as usize
                );
                assert_eq!(data_3.data.len(), 820);

                // check data consistency
                assert_eq!(
                    &data_1.data[..],
                    (0..MINIMUM_PDU_SIZE - PDU_HEADER_SIZE).map(|x| x as u8).collect::<Vec<_>>()
                );
                assert_eq!(
                    data_1.data.len() + data_2.data.len() + data_3.data.len(),
                    9000
                );

                let data_1 = &data_1.data;
                let data_2 = &data_2.data;
                let data_3 = &data_3.data;

                let mut all_data: Vec<u8> = Vec::new();
                all_data.extend(data_1);
                all_data.extend(data_2);
                all_data.extend(data_3);
                assert_eq!(all_data, my_data);
            }
            x => panic!("Expected 3 PDatas, got {:?}", x),
        }

        assert_eq!(cursor.len(), 0);
    }
}
<commit_msg>Improve PDataWriter implementation<commit_after>use std::io::Write;

use crate::pdu::reader::PDU_HEADER_SIZE;

/// A P-Data value writer.
///
/// This exposes an API to iteratively construct and send Data messages
/// to another node.
/// Using this as a [standard writer](std::io::Write)
/// will automatically split the incoming bytes
/// into separate PDUs if they do not fit in a single one.
///
/// # Example
///
/// Use an association's `send_pdata` method
/// to create a new P-Data value writer.
///
/// ```no_run
/// # use std::io::Write;
/// # use dicom_ul::association::{ClientAssociationOptions, PDataWriter};
/// # use dicom_ul::pdu::{Pdu, PDataValue, PDataValueType};
/// # fn command_data() -> Vec<u8> { unimplemented!() }
/// # fn dicom_data() -> &'static [u8] { unimplemented!() }
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut association = ClientAssociationOptions::new()
///    .establish("129.168.0.5:104")?;
///
/// let presentation_context_id = association.presentation_contexts()[0].id;
///
/// // send a command first
/// association.send(&Pdu::PData {
///     data: vec![PDataValue {
///     presentation_context_id,
///     value_type: PDataValueType::Command,
///         is_last: true,
///         data: command_data(),
///     }],
/// });
///
/// // then send a DICOM object which may be split into multiple PDUs
/// let mut pdata = association.send_pdata(presentation_context_id);
/// pdata.write_all(dicom_data())?;
/// pdata.finish()?;
///
/// let pdu_ac = association.receive()?;
/// # Ok(())
/// # }
#[must_use]
pub struct PDataWriter<W: Write> {
    buffer: Vec<u8>,
    stream: W,
    max_data_len: u32,
}

impl<W> PDataWriter<W>
where
    W: Write,
{
    /// Construct a new P-Data value writer.
    ///
    /// `max_pdu_length` is the maximum value of the PDU-length property.
    pub(crate) fn new(stream: W, presentation_context_id: u8, max_pdu_length: u32) -> Self {
        let max_data_length = calculate_max_data_len_single(max_pdu_length);
        let mut buffer = Vec::with_capacity((max_data_length + PDU_HEADER_SIZE) as usize);
        // initial buffer set up
        buffer.extend([
            // PDU-type + reserved byte
            0x04,
            0x00,
            // full PDU length, unknown at this point
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            // presentation data length, unknown at this point
            0xFF,
            0xFF,
            0xFF,
            0xFF,
            // presentation context id
            presentation_context_id,
            // message control header, unknown at this point
            0xFF,
        ]);

        PDataWriter {
            stream,
            max_data_len: max_data_length,
            buffer,
        }
    }

    /// Declare to have finished sending P-Data fragments,
    /// thus emitting the last P-Data fragment PDU.
    ///
    /// This is also done automatically once the P-Data writer is dropped.
    pub fn finish(mut self) -> std::io::Result<()> {
        self.finish_impl()?;
        Ok(())
    }

    /// Set up the P-Data PDU header for sending.
    fn setup_pdata_header(&mut self, is_last: bool) {
        let data_len = (self.buffer.len() - 12) as u32;

        // full PDU length (minus PDU type and reserved byte)
        let pdu_len = data_len + 4 + 2;
        let pdu_len_bytes = pdu_len.to_be_bytes();

        self.buffer[2] = pdu_len_bytes[0];
        self.buffer[3] = pdu_len_bytes[1];
        self.buffer[4] = pdu_len_bytes[2];
        self.buffer[5] = pdu_len_bytes[3];

        // presentation data length (data + 2 properties below)
        let pdv_data_len = data_len + 2;
        let data_len_bytes = pdv_data_len.to_be_bytes();

        self.buffer[6] = data_len_bytes[0];
        self.buffer[7] = data_len_bytes[1];
        self.buffer[8] = data_len_bytes[2];
        self.buffer[9] = data_len_bytes[3];

        // message control header
        self.buffer[11] = if is_last { 0x02 } else { 0x00 };
    }

    fn finish_impl(&mut self) -> std::io::Result<()> {
        if !self.buffer.is_empty() {
            // send last PDU
            self.setup_pdata_header(true);
            self.stream.write_all(&self.buffer[..])?;
            // clear buffer so that subsequent calls to `finish_impl`
            // do not send any more PDUs
            self.buffer.clear();
        }
        Ok(())
    }

    /// Use the current state of the buffer to send new PDUs
    ///
    /// Pre-condition:
    /// buffer must have enough data for one P-Data-tf PDU
    fn dispatch_pdu(&mut self) -> std::io::Result<()> {
        debug_assert!(self.buffer.len() >= 12);
        // send PDU now
        self.setup_pdata_header(false);
        self.stream.write_all(&self.buffer)?;

        // back to just the header
        self.buffer.truncate(12);

        Ok(())
    }
}

impl<W> Write for PDataWriter<W>
where
    W: Write,
{
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        let total_len = self.max_data_len as usize + 12;
        if self.buffer.len() + buf.len() <= total_len {
            // accumulate into buffer, do nothing
            self.buffer.extend(buf);
            Ok(buf.len())
        } else {
            // fill in the rest of the buffer, send PDU,
            // and leave out the rest for subsequent writes
            let buf = &buf[..total_len - self.buffer.len()];
            self.buffer.extend(buf);
            debug_assert_eq!(self.buffer.len(), total_len);
            self.dispatch_pdu()?;
            Ok(buf.len())
        }
    }

    fn flush(&mut self) -> std::io::Result<()> {
        // do nothing
        Ok(())
    }
}

/// With the P-Data writer dropped,
/// this `Drop` implementation
/// will construct and emit the last P-Data fragment PDU
/// if there is any data left to send.
impl<W> Drop for PDataWriter<W>
where
    W: Write,
{
    fn drop(&mut self) {
        let _ = self.finish_impl();
    }
}

/// Determine the maximum length of actual PDV data
/// when encapsulated in a PDU with the given length property.
/// Does not account for the first 2 bytes (type + reserved).
#[inline]
fn calculate_max_data_len_single(pdu_len: u32) -> u32 {
    // data length: 4 bytes
    // control header: 2 bytes
    pdu_len - 4 - 2
}

#[cfg(test)]
mod tests {
    use std::io::Write;

    use crate::pdu::reader::{read_pdu, MINIMUM_PDU_SIZE, PDU_HEADER_SIZE};
    use crate::pdu::PDataValueType;
    use crate::pdu::Pdu;

    use super::PDataWriter;

    #[test]
    fn test_write_pdata_and_finish() {
        let presentation_context_id = 12;

        let mut buf = Vec::new();
        {
            let mut writer = PDataWriter::new(&mut buf, presentation_context_id, MINIMUM_PDU_SIZE);
            writer.write_all(&(0..64).collect::<Vec<u8>>()).unwrap();
            writer.finish().unwrap();
        }

        let mut cursor = &buf[..];
        let same_pdu = read_pdu(&mut cursor, MINIMUM_PDU_SIZE, true).unwrap();

        // concatenate data chunks, compare with all data

        match same_pdu {
            Pdu::PData { data: data_1 } => {
                let data_1 = &data_1[0];

                // check that this PDU is consistent
                assert_eq!(data_1.value_type, PDataValueType::Data);
                assert_eq!(data_1.presentation_context_id, presentation_context_id);
                assert_eq!(data_1.data.len(), 64);
                assert_eq!(data_1.data, (0..64).collect::<Vec<u8>>());
            }
            pdu => panic!("Expected PData, got {:?}", pdu),
        }

        assert_eq!(cursor.len(), 0);
    }

    #[test]
    fn test_write_large_pdata_and_finish() {
        let presentation_context_id = 32;

        let my_data: Vec<_> = (0..9000).map(|x: u32| x as u8).collect();

        let mut buf = Vec::new();
        {
            let mut writer = PDataWriter::new(&mut buf, presentation_context_id, MINIMUM_PDU_SIZE);
            writer.write_all(&my_data).unwrap();
            writer.finish().unwrap();
        }

        let mut cursor = &buf[..];
        let pdu_1 = read_pdu(&mut cursor, MINIMUM_PDU_SIZE, true).unwrap();
        let pdu_2 = read_pdu(&mut cursor, MINIMUM_PDU_SIZE, true).unwrap();
        let pdu_3 = read_pdu(&mut cursor, MINIMUM_PDU_SIZE, true).unwrap();

        // concatenate data chunks, compare with all data

        match (pdu_1, pdu_2, pdu_3) {
            (
                Pdu::PData { data: data_1 },
                Pdu::PData { data: data_2 },
                Pdu::PData { data: data_3 },
            ) => {
                assert_eq!(data_1.len(), 1);
                let data_1 = &data_1[0];
                assert_eq!(data_2.len(), 1);
                let data_2 = &data_2[0];
                assert_eq!(data_3.len(), 1);
                let data_3 = &data_3[0];

                // check that these two PDUs are consistent
                assert_eq!(data_1.value_type, PDataValueType::Data);
                assert_eq!(data_2.value_type, PDataValueType::Data);
                assert_eq!(data_1.presentation_context_id, presentation_context_id);
                assert_eq!(data_2.presentation_context_id, presentation_context_id);

                // check expected lengths
                assert_eq!(
                    data_1.data.len(),
                    (MINIMUM_PDU_SIZE - PDU_HEADER_SIZE) as usize
                );
                assert_eq!(
                    data_2.data.len(),
                    (MINIMUM_PDU_SIZE - PDU_HEADER_SIZE) as usize
                );
                assert_eq!(data_3.data.len(), 820);

                // check data consistency
                assert_eq!(
                    &data_1.data[..],
                    (0..MINIMUM_PDU_SIZE - PDU_HEADER_SIZE)
                        .map(|x| x as u8)
                        .collect::<Vec<_>>()
                );
                assert_eq!(
                    data_1.data.len() + data_2.data.len() + data_3.data.len(),
                    9000
                );

                let data_1 = &data_1.data;
                let data_2 = &data_2.data;
                let data_3 = &data_3.data;

                let mut all_data: Vec<u8> = Vec::new();
                all_data.extend(data_1);
                all_data.extend(data_2);
                all_data.extend(data_3);
                assert_eq!(all_data, my_data);
            }
            x => panic!("Expected 3 PDatas, got {:?}", x),
        }

        assert_eq!(cursor.len(), 0);
    }
}
<|endoftext|>


<commit_before>use std::cmp::PartialEq;
use std::convert::AsRef;
use std::fmt::{self, Debug};
use std::ops::Deref;

use super::*;

/// A reference to a heap location that is
/// guaranteed to be valid for as long as this
/// value exists.
pub struct PinnedValue(*const u8, usize, Guard);

impl Deref for PinnedValue {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        unsafe { std::slice::from_raw_parts(self.0, self.1) }
    }
}

impl PartialEq for PinnedValue {
    fn eq(&self, other: &PinnedValue) -> bool {
        use std::ops::Deref;
        self.deref() == other.deref()
    }
}

impl PartialEq<[u8]> for PinnedValue {
    fn eq(&self, other: &[u8]) -> bool {
        self.deref() == other
    }
}

impl<'a> PartialEq<&'a [u8]> for PinnedValue {
    fn eq(&self, other: &&[u8]) -> bool {
        self.deref() == *other
    }
}

impl PartialEq<Value> for PinnedValue {
    fn eq(&self, other: &Value) -> bool {
        use std::ops::Deref;
        self.deref() == &**other
    }
}

impl AsRef<[u8]> for PinnedValue {
    fn as_ref(&self) -> &[u8] {
        self.deref()
    }
}

impl Debug for PinnedValue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "PinnedValue( {:?}, Guard )", self.deref())
    }
}

impl PinnedValue {
    pub(crate) fn new(v: &[u8], guard: Guard) -> PinnedValue {
        let ptr = v.as_ptr();
        PinnedValue(ptr, v.len(), guard)
    }
}
<commit_msg>Simplify PinnedValue PartialEq a little<commit_after>use std::cmp::PartialEq;
use std::convert::AsRef;
use std::fmt::{self, Debug};
use std::ops::Deref;

use super::*;

/// A reference to a heap location that is
/// guaranteed to be valid for as long as this
/// value exists.
pub struct PinnedValue(*const u8, usize, Guard);

impl Deref for PinnedValue {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        unsafe { std::slice::from_raw_parts(self.0, self.1) }
    }
}

impl PartialEq for PinnedValue {
    fn eq(&self, other: &PinnedValue) -> bool {
        use std::ops::Deref;
        self.deref() == other.deref()
    }
}

impl<T: AsRef<[u8]>> PartialEq<T> for PinnedValue {
    fn eq(&self, other: &T) -> bool {
        self.deref() == other.as_ref()
    }
}

impl PartialEq<[u8]> for PinnedValue {
    fn eq(&self, other: &[u8]) -> bool {
        self.deref() == other
    }
}

impl Debug for PinnedValue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "PinnedValue( {:?}, Guard )", self.deref())
    }
}

impl PinnedValue {
    pub(crate) fn new(v: &[u8], guard: Guard) -> PinnedValue {
        let ptr = v.as_ptr();
        PinnedValue(ptr, v.len(), guard)
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate rustfbp;
extern crate capnp;

#[derive(Debug)]
struct Graph {
    nodes: Vec<(String, String)>,
    edges: Vec<(String, String, String, String, String, String)>,
    imsgs: Vec<(String, String, String, String)>,
    ext_in: Vec<(String, String, String, String)>,
    ext_out: Vec<(String, String, String, String)>,
}

#[derive(PartialEq, Debug)]
enum State {
    Break, Compo, Port, IMSG,
    CompPort, CompPortBind, CompPortBindPort,
    CompPortExternal, CompPortExternalPort,
    PortExternal, PortExternalPort,
    IMSGBind, IMSGBindPort,
    ErrorS
}
use State::*;

enum Literal {
    Comp(String, String),
    Port(String, String),
    IMSG(String),
    Bind, External,
}

agent! {
    input(input: fbp_lexical),
    output(output: fbp_graph, error: fbp_semantic_error),
    fn run(&mut self) -> Result<Signal> {
        let mut msg = try!(self.input.input.recv());
        let literal: fbp_lexical::Reader = try!(msg.read_schema());
        let literal = try!(literal.which());

        match literal {
            fbp_lexical::Start(path) => {
                match handle_stream(&self)? {
                    Ok(graph) => { send_graph(&self, try!(path), &graph)? },
                    Err(errors) => {
                        let mut new_msg = Msg::new();
                        {
                            let mut msg = new_msg.build_schema::<fbp_semantic_error::Builder>();
                            msg.get_path()?.set_text(path?);
                            {
                                let mut nodes = msg.init_parsing(errors.len() as u32);
                                let mut i = 0;
                                for n in &errors {
                                    nodes.borrow().set(i, &n[..]);
                                    i += 1;
                                }
                            }
                        }
                        let _ = self.output.error.send(new_msg);
                    },
                }
            }
            _ => { return Err(result::Error::Misc("bad stream".to_string())); },
        }
        Ok(End)
    }
}

fn handle_stream(comp: &ThisAgent) -> Result<std::result::Result<Graph, Vec<String>>> {
    let mut state = Break;
    let mut stack: Vec<Literal> = vec![];
    let mut graph = Graph {
        nodes: vec![],
        edges: vec![],
        imsgs: vec![],
        ext_in: vec![],
        ext_out: vec![]
    };
    let mut errors: Vec<String> = vec![];
    let mut line: usize = 1;

    loop {

        let mut msg = comp.input.input.recv()?;
        let literal: fbp_lexical::Reader = msg.read_schema()?;
        let literal = literal.which()?;
        match literal {
            fbp_lexical::End(_) => {
                break;
            },
            fbp_lexical::Token(t) => {
                let token = t.which()?;
                match token {
                    fbp_lexical::token::Bind(_) => {
                        state = match state {
                            CompPort => { CompPortBind },
                            ErrorS => { ErrorS },
                            IMSG => { IMSGBind },
                            _ => {
                                errors.push(format!("line {} : Found a \"->\", when \"{}\" was expected.", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::External(_) => {
                        state = match state {
                            CompPort => { CompPortExternal },
                            Port => { PortExternal },
                            ErrorS => { ErrorS },
                            _ => {
                                errors.push(format!("line {} : Found a \"=>\", when \"{}\" was expected.", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Port(port) => {
                        stack.push(Literal::Port(port.get_name()?.get_text()?.to_string(), port.get_selection()?.get_text()?.to_string()));
                        state = match state {
                            Compo => { CompPort },
                            CompPortBind => { CompPortBindPort },
                            CompPortExternal => {
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_p_n, _) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(ref n, ref s) = out_c { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_out.push((out_c_n.clone(), out_p_n, out_p_s, in_p_n));
                                }
                                CompPortExternalPort },
                            Break => { Port },
                            PortExternal => { PortExternalPort },
                            ErrorS => { ErrorS },
                            IMSGBind => { IMSGBindPort },
                            _ => {
                                errors.push(format!("line {} : Found port \"{}[{}]\", when \"{}\" was expected.", line, port.get_name()?.get_text()?, port.get_selection()?.get_text()?, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Comp(comp) => {
                        if comp.get_sort()?.get_text()? != "" {
                            graph.nodes.push((comp.get_name()?.get_text()?.to_string(), comp.get_sort()?.get_text()?.to_string()));
                        }
                        stack.push(Literal::Comp(comp.get_name()?.get_text()?.to_string(), comp.get_sort()?.get_text()?.to_string()));
                        state = match state {
                            CompPortBindPort => {
                                let in_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(n, s) = out_c { (n, s) } else { unreachable!() };
                                    graph.edges.push((out_c_n, out_p_n, out_p_s, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Compo
                            },
                            PortExternalPort => {
                                let in_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_in.push((out_p_n, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Compo
                            }
                            IMSGBindPort => {
                                let in_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let imsg = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let imsg = if let Literal::IMSG(imsg) = imsg { imsg } else { unreachable!() };
                                    graph.imsgs.push((imsg, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Compo
                            }
                            Break => { Compo },
                            ErrorS => { stack = vec![stack.pop().ok_or(result::Error::Misc("stack problem".into()))?]; Compo },
                            _ => {
                                errors.push(format!("line {} : Found agent \"{}({})\", when \"{}\" was expected.", line, comp.get_name()?.get_text()?, comp.get_sort()?.get_text()?, get_expected(&state)));
                                ErrorS
                            },
                        }
                    },
                    fbp_lexical::token::Imsg(imsg) => {
                        let imsg = imsg?;
                        stack.push(Literal::IMSG(imsg.get_text()?.to_string()));
                        state = match state {
                            ErrorS => { IMSG },
                            Break => { IMSG },
                            _ => {
                                errors.push(format!("line {} : Found an IMSG \"{}\", when \"{}\" was expected.", line, imsg, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Break(_) => {
                        line += 1;
                        state = match state {
                            CompPortBind => { state },
                            IMSGBind => { state },
                            Compo => { stack.clear(); Break },
                            CompPortExternalPort => { stack.clear(); Break },
                            Break => { Break },
                            ErrorS => { ErrorS },
                            _ => {
                                errors.push(format!("line {} : Found a \"\n\" or (new line), when \"{}\" was expected.", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    }
                }
            },
            _ => { panic!("bad stream"); },
        }
    }
    if errors.len() > 0 {
        Ok(Err(errors))
    } else {
        Ok(Ok(graph))
    }
}

fn get_expected(state: &State) -> String {
    match *state {
        Break => { "[Component, Port, IMSG, NewLine]".into() },
        Compo => { "[Port, NewLine]".into() },
        Port => { "[Component, ->, =>]".into() },
        IMSG => { "[->]".into() },
        CompPort => { "[->, =>]".into() },
        CompPortBind => { "[Port]".into() },
        CompPortBindPort => { "[Component]".into() },
        CompPortExternal => { "[Port]".into() },
        CompPortExternalPort => { "[NewLine]".into() },
        PortExternal => { "[Port]".into() },
        PortExternalPort => { "[Component]".into() },
        IMSGBind => { "[Port]".into() },
        IMSGBindPort => { "[Component]".into() },
        ErrorS => { unreachable!() }
    }
}

fn send_graph(comp: &ThisAgent, path: &str, graph: &Graph) -> Result<()> {
    let mut new_msg = Msg::new();
    {
        let mut msg = new_msg.build_schema::<fbp_graph::Builder>();
        msg.set_path(path);
        {
            let mut nodes = msg.borrow().init_nodes(graph.nodes.len() as u32);
            let mut i = 0;
            for n in &graph.nodes {
                nodes.borrow().get(i).set_name(&n.0[..]);
                nodes.borrow().get(i).set_sort(&n.1[..]);
                i += 1;
            }
        }
        {
            let mut edges = msg.borrow().init_edges(graph.edges.len() as u32);
            let mut i = 0;
            for e in &graph.edges {
                edges.borrow().get(i).set_o_name(&e.0[..]);
                edges.borrow().get(i).set_o_port(&e.1[..]);
                edges.borrow().get(i).set_o_selection(&e.2[..]);
                edges.borrow().get(i).set_i_port(&e.3[..]);
                edges.borrow().get(i).set_i_selection(&e.4[..]);
                edges.borrow().get(i).set_i_name(&e.5[..]);
                i += 1;
            }
        }
        {
            let mut imsgs = msg.borrow().init_imsgs(graph.imsgs.len() as u32);
            let mut i = 0;
            for imsg in &graph.imsgs {
                imsgs.borrow().get(i).set_imsg(&imsg.0[..]);
                imsgs.borrow().get(i).set_port(&imsg.1[..]);
                imsgs.borrow().get(i).set_selection(&imsg.2[..]);
                imsgs.borrow().get(i).set_comp(&imsg.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = msg.borrow().init_external_inputs(graph.ext_in.len() as u32);
            let mut i = 0;
            for e in &graph.ext_in {
                ext.borrow().get(i).set_name(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_comp(&e.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = msg.borrow().init_external_outputs(graph.ext_out.len() as u32);
            let mut i = 0;
            for e in &graph.ext_out {
                ext.borrow().get(i).set_comp(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_name(&e.3[..]);
                i += 1;
            }
        }
    }
    let _ = comp.output.output.send(new_msg);
    Ok(())
}
<commit_msg>Problem: core_parser_semantic didn't type check<commit_after>#[macro_use]
extern crate rustfbp;
extern crate capnp;

#[derive(Debug)]
struct Graph {
    nodes: Vec<(String, String)>,
    edges: Vec<(String, String, String, String, String, String)>,
    imsgs: Vec<(String, String, String, String)>,
    ext_in: Vec<(String, String, String, String)>,
    ext_out: Vec<(String, String, String, String)>,
}

#[derive(PartialEq, Debug)]
enum State {
    Break, Compo, Port, IMSG,
    CompPort, CompPortBind, CompPortBindPort,
    CompPortExternal, CompPortExternalPort,
    PortExternal, PortExternalPort,
    IMSGBind, IMSGBindPort,
    ErrorS
}
use State::*;

enum Literal {
    Comp(String, String),
    Port(String, String),
    IMSG(String),
    Bind, External,
}

agent! {
    input(input: fbp_lexical),
    output(output: fbp_graph, error: fbp_semantic_error),
    fn run(&mut self) -> Result<Signal> {
        let mut msg = self.input.input.recv()?;
        let literal: fbp_lexical::Reader = msg.read_schema()?;
        let literal = literal.which()?;

        match literal {
            fbp_lexical::Start(path) => {
                match handle_stream(&self)? {
                    Ok(graph) => { send_graph(&self, path?.get_text()?, &graph)? },
                    Err(errors) => {
                        let mut new_msg = Msg::new();
                        {
                            let mut msg = new_msg.build_schema::<fbp_semantic_error::Builder>();
                            // msg.get_path()?.set_text(path?);
                            msg.borrow().get_path()?.set_text(path?.get_text()?);
                            {
                                let mut nodes = msg.borrow().init_parsing().init_list(errors.len() as u32);
                                let mut i = 0;
                                for n in &errors {
                                    nodes.borrow().get(i).set_text(&n[..]);
                                    i += 1;
                                }
                            }
                        }
                        let _ = self.output.error.send(new_msg);
                    },
                }
            }
            _ => { return Err(result::Error::Misc("bad stream".to_string())); },
        }
        Ok(End)
    }
}

fn handle_stream(comp: &ThisAgent) -> Result<std::result::Result<Graph, Vec<String>>> {
    let mut state = Break;
    let mut stack: Vec<Literal> = vec![];
    let mut graph = Graph {
        nodes: vec![],
        edges: vec![],
        imsgs: vec![],
        ext_in: vec![],
        ext_out: vec![]
    };
    let mut errors: Vec<String> = vec![];
    let mut line: usize = 1;

    loop {

        let mut msg = comp.input.input.recv()?;
        let literal: fbp_lexical::Reader = msg.read_schema()?;
        let literal = literal.which()?;
        match literal {
            fbp_lexical::End(_) => {
                break;
            },
            fbp_lexical::Token(t) => {
                let token = t.which()?;
                match token {
                    fbp_lexical::token::Bind(_) => {
                        state = match state {
                            CompPort => { CompPortBind },
                            ErrorS => { ErrorS },
                            IMSG => { IMSGBind },
                            _ => {
                                errors.push(format!("line {} : Found a \"->\", when \"{}\" was expected.", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::External(_) => {
                        state = match state {
                            CompPort => { CompPortExternal },
                            Port => { PortExternal },
                            ErrorS => { ErrorS },
                            _ => {
                                errors.push(format!("line {} : Found a \"=>\", when \"{}\" was expected.", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Port(port) => {
                        stack.push(Literal::Port(port.get_name()?.get_text()?.to_string(), port.get_selection()?.get_text()?.to_string()));
                        state = match state {
                            Compo => { CompPort },
                            CompPortBind => { CompPortBindPort },
                            CompPortExternal => {
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_p_n, _) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(ref n, ref s) = out_c { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_out.push((out_c_n.clone(), out_p_n, out_p_s, in_p_n));
                                }
                                CompPortExternalPort },
                            Break => { Port },
                            PortExternal => { PortExternalPort },
                            ErrorS => { ErrorS },
                            IMSGBind => { IMSGBindPort },
                            _ => {
                                errors.push(format!("line {} : Found port \"{}[{}]\", when \"{}\" was expected.", line, port.get_name()?.get_text()?, port.get_selection()?.get_text()?, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Comp(comp) => {
                        if comp.get_sort()?.get_text()? != "" {
                            graph.nodes.push((comp.get_name()?.get_text()?.to_string(), comp.get_sort()?.get_text()?.to_string()));
                        }
                        stack.push(Literal::Comp(comp.get_name()?.get_text()?.to_string(), comp.get_sort()?.get_text()?.to_string()));
                        state = match state {
                            CompPortBindPort => {
                                let in_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    let (out_c_n, _) = if let Literal::Comp(n, s) = out_c { (n, s) } else { unreachable!() };
                                    graph.edges.push((out_c_n, out_p_n, out_p_s, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Compo
                            },
                            PortExternalPort => {
                                let in_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let out_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let (out_p_n, out_p_s) = if let Literal::Port(n, s) = out_p { (n, s) } else { unreachable!() };
                                    graph.ext_in.push((out_p_n, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Compo
                            }
                            IMSGBindPort => {
                                let in_c = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let in_p = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                let imsg = stack.pop().ok_or(result::Error::Misc("stack problem".into()))?;
                                {
                                    let (in_c_n, _) = if let Literal::Comp(ref n, ref s) = in_c { (n, s) } else { unreachable!() };
                                    let (in_p_n, in_p_s) = if let Literal::Port(n, s) = in_p { (n, s) } else { unreachable!() };
                                    let imsg = if let Literal::IMSG(imsg) = imsg { imsg } else { unreachable!() };
                                    graph.imsgs.push((imsg, in_p_n, in_p_s, in_c_n.clone()));
                                }
                                stack.push(in_c);
                                Compo
                            }
                            Break => { Compo },
                            ErrorS => { stack = vec![stack.pop().ok_or(result::Error::Misc("stack problem".into()))?]; Compo },
                            _ => {
                                errors.push(format!("line {} : Found agent \"{}({})\", when \"{}\" was expected.", line, comp.get_name()?.get_text()?, comp.get_sort()?.get_text()?, get_expected(&state)));
                                ErrorS
                            },
                        }
                    },
                    fbp_lexical::token::Imsg(imsg) => {
                        let imsg = imsg?;
                        stack.push(Literal::IMSG(imsg.get_text()?.to_string()));
                        state = match state {
                            ErrorS => { IMSG },
                            Break => { IMSG },
                            _ => {
                                errors.push(format!("line {} : Found an IMSG \"{}\", when \"{}\" was expected.", line, imsg.get_text()?, get_expected(&state)));
                                ErrorS
                            },
                        };
                    },
                    fbp_lexical::token::Break(_) => {
                        line += 1;
                        state = match state {
                            CompPortBind => { state },
                            IMSGBind => { state },
                            Compo => { stack.clear(); Break },
                            CompPortExternalPort => { stack.clear(); Break },
                            Break => { Break },
                            ErrorS => { ErrorS },
                            _ => {
                                errors.push(format!("line {} : Found a \"\n\" or (new line), when \"{}\" was expected.", line, get_expected(&state)));
                                ErrorS
                            },
                        };
                    }
                }
            },
            _ => { panic!("bad stream"); },
        }
    }
    if errors.len() > 0 {
        Ok(Err(errors))
    } else {
        Ok(Ok(graph))
    }
}

fn get_expected(state: &State) -> String {
    match *state {
        Break => { "[Component, Port, IMSG, NewLine]".into() },
        Compo => { "[Port, NewLine]".into() },
        Port => { "[Component, ->, =>]".into() },
        IMSG => { "[->]".into() },
        CompPort => { "[->, =>]".into() },
        CompPortBind => { "[Port]".into() },
        CompPortBindPort => { "[Component]".into() },
        CompPortExternal => { "[Port]".into() },
        CompPortExternalPort => { "[NewLine]".into() },
        PortExternal => { "[Port]".into() },
        PortExternalPort => { "[Component]".into() },
        IMSGBind => { "[Port]".into() },
        IMSGBindPort => { "[Component]".into() },
        ErrorS => { unreachable!() }
    }
}

fn send_graph(comp: &ThisAgent, path: &str, graph: &Graph) -> Result<()> {
    let mut new_msg = Msg::new();
    {
        let mut msg = new_msg.build_schema::<fbp_graph::Builder>();
        msg.set_path(path);
        {
            let mut nodes = msg.borrow().init_nodes(graph.nodes.len() as u32);
            let mut i = 0;
            for n in &graph.nodes {
                nodes.borrow().get(i).set_name(&n.0[..]);
                nodes.borrow().get(i).set_sort(&n.1[..]);
                i += 1;
            }
        }
        {
            let mut edges = msg.borrow().init_edges(graph.edges.len() as u32);
            let mut i = 0;
            for e in &graph.edges {
                edges.borrow().get(i).set_o_name(&e.0[..]);
                edges.borrow().get(i).set_o_port(&e.1[..]);
                edges.borrow().get(i).set_o_selection(&e.2[..]);
                edges.borrow().get(i).set_i_port(&e.3[..]);
                edges.borrow().get(i).set_i_selection(&e.4[..]);
                edges.borrow().get(i).set_i_name(&e.5[..]);
                i += 1;
            }
        }
        {
            let mut imsgs = msg.borrow().init_imsgs(graph.imsgs.len() as u32);
            let mut i = 0;
            for imsg in &graph.imsgs {
                imsgs.borrow().get(i).set_imsg(&imsg.0[..]);
                imsgs.borrow().get(i).set_port(&imsg.1[..]);
                imsgs.borrow().get(i).set_selection(&imsg.2[..]);
                imsgs.borrow().get(i).set_comp(&imsg.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = msg.borrow().init_external_inputs(graph.ext_in.len() as u32);
            let mut i = 0;
            for e in &graph.ext_in {
                ext.borrow().get(i).set_name(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_comp(&e.3[..]);
                i += 1;
            }
        }
        {
            let mut ext = msg.borrow().init_external_outputs(graph.ext_out.len() as u32);
            let mut i = 0;
            for e in &graph.ext_out {
                ext.borrow().get(i).set_comp(&e.0[..]);
                ext.borrow().get(i).set_port(&e.1[..]);
                ext.borrow().get(i).set_selection(&e.2[..]);
                ext.borrow().get(i).set_name(&e.3[..]);
                i += 1;
            }
        }
    }
    let _ = comp.output.output.send(new_msg);
    Ok(())
}
<|endoftext|>


<commit_before>use super::{Open, Sink};
use alsa::device_name::HintIter;
use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
use alsa::{Direction, Error, ValueOr};
use std::cmp::min;
use std::ffi::CString;
use std::io;
use std::process::exit;

const PERIOD_SIZE: usize = 5512; // Period of roughly 125ms
const BUFFERED_PERIODS: usize = 4; // ~ 0.5s latency

pub struct AlsaSink {
    pcm: Option<PCM>,
    device: String,
    buffer: [i16; PERIOD_SIZE as usize],
    buffered_data: usize,
}

fn list_outputs() {
    for t in &["pcm", "ctl", "hwdep"] {
        println!("{} devices:", t);
        let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
        for a in i {
            if let Some(Direction::Playback) = a.direction {
                // mimic aplay -L
                println!(
                    "{}\n\t{}\n",
                    a.name.unwrap(),
                    a.desc.unwrap().replace("\n", "\n\t")
                );
            }
        }
    }
}

fn open_device(dev_name: &str) -> Result<(PCM), Box<Error>> {
    let pcm = PCM::new(dev_name, Direction::Playback, false)?;
    // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
    // latency = period_size * periods / (rate * bytes_per_frame)
    // For 16 Bit stereo data, one frame has a length of four bytes.
    // 500ms  = buffer_size / (44100 * 4)
    // buffer_size_bytes = 0.5 * 44100 / 4
    // buffer_size_frames = 0.5 * 44100 = 22050
    {
        // Set hardware parameters: 44100 Hz / Stereo / 16 bit
        let hwp = HwParams::any(&pcm)?;

        hwp.set_access(Access::RWInterleaved)?;
        hwp.set_format(Format::s16())?;
        hwp.set_rate(44100, ValueOr::Nearest)?;
        hwp.set_channels(2)?;
        hwp.set_period_size_near(PERIOD_SIZE as Frames, ValueOr::Nearest)?;
        hwp.set_buffer_size_near((PERIOD_SIZE * BUFFERED_PERIODS) as Frames)?;
        pcm.hw_params(&hwp)?;

        let swp = pcm.sw_params_current()?;
        swp.set_start_threshold(hwp.get_buffer_size()? - hwp.get_period_size()?)?;
        pcm.sw_params(&swp)?;
    }

    Ok(pcm)
}

impl Open for AlsaSink {
    fn open(device: Option<String>) -> AlsaSink {
        info!("Using alsa sink");

        let name = match device.as_ref().map(AsRef::as_ref) {
            Some("?") => {
                println!("Listing available alsa outputs");
                list_outputs();
                exit(0)
            }
            Some(device) => device,
            None => "default",
        }
        .to_string();

        AlsaSink {
            pcm: None,
            device: name,
            buffer: [0; PERIOD_SIZE],
            buffered_data: 0,
        }
    }
}

impl Sink for AlsaSink {
    fn start(&mut self) -> io::Result<()> {
        if self.pcm.is_none() {
            let pcm = open_device(&self.device);
            match pcm {
                Ok(p) => self.pcm = Some(p),
                Err(e) => {
                    error!("Alsa error PCM open {}", e);
                    return Err(io::Error::new(
                        io::ErrorKind::Other,
                        "Alsa error: PCM open failed",
                    ));
                }
            }
        }

        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        {
            let pcm = self.pcm.as_mut().unwrap();
            // Write any leftover data in the period buffer
            // before draining the actual buffer
            let io = pcm.io_i16().unwrap();
            match io.writei(&self.buffer[..self.buffered_data]) {
                Ok(_) => (),
                Err(err) => pcm.try_recover(err, false).unwrap(),
            }
            pcm.drain().unwrap();
        }
        self.pcm = None;
        self.buffered_data = 0;
        Ok(())
    }

    fn write(&mut self, data: &[i16]) -> io::Result<()> {
        let mut processed_data = 0;
        while processed_data < data.len() {
            let data_to_buffer = min(
                PERIOD_SIZE - self.buffered_data,
                data.len() - processed_data,
            );
            let buffer_slice =
                &mut self.buffer[self.buffered_data..self.buffered_data + data_to_buffer];
            buffer_slice.copy_from_slice(&data[processed_data..processed_data + data_to_buffer]);
            self.buffered_data += data_to_buffer;
            processed_data += data_to_buffer;
            if self.buffered_data == PERIOD_SIZE {
                self.buffered_data = 0;
                let pcm = self.pcm.as_mut().unwrap();
                let io = pcm.io_i16().unwrap();
                match io.writei(&self.buffer) {
                    Ok(_) => (),
                    Err(err) => pcm.try_recover(err, false).unwrap(),
                }
            }
        }

        Ok(())
    }
}
<commit_msg>Rewrite buffer around the actual period size<commit_after>use super::{Open, Sink};
use alsa::device_name::HintIter;
use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
use alsa::{Direction, Error, ValueOr};
use std::cmp::min;
use std::ffi::CString;
use std::io;
use std::process::exit;

const PREFERED_PERIOD_SIZE: Frames = 5512; // Period of roughly 125ms
const BUFFERED_PERIODS: Frames = 4;

pub struct AlsaSink {
    pcm: Option<PCM>,
    device: String,
    buffer: Vec<i16>,
}

fn list_outputs() {
    for t in &["pcm", "ctl", "hwdep"] {
        println!("{} devices:", t);
        let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
        for a in i {
            if let Some(Direction::Playback) = a.direction {
                // mimic aplay -L
                println!(
                    "{}\n\t{}\n",
                    a.name.unwrap(),
                    a.desc.unwrap().replace("\n", "\n\t")
                );
            }
        }
    }
}

fn open_device(dev_name: &str) -> Result<(PCM, Frames), Box<Error>> {
    let pcm = PCM::new(dev_name, Direction::Playback, false)?;
    let period_size = PREFERED_PERIOD_SIZE;
    // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
    // latency = period_size * periods / (rate * bytes_per_frame)
    // For 16 Bit stereo data, one frame has a length of four bytes.
    // 500ms  = buffer_size / (44100 * 4)
    // buffer_size_bytes = 0.5 * 44100 / 4
    // buffer_size_frames = 0.5 * 44100 = 22050
    {
        // Set hardware parameters: 44100 Hz / Stereo / 16 bit
        let hwp = HwParams::any(&pcm)?;

        hwp.set_access(Access::RWInterleaved)?;
        hwp.set_format(Format::s16())?;
        hwp.set_rate(44100, ValueOr::Nearest)?;
        hwp.set_channels(2)?;
        let period_size = hwp.set_period_size_near(period_size, ValueOr::Greater)?;
        hwp.set_buffer_size_near(period_size * BUFFERED_PERIODS)?;
        pcm.hw_params(&hwp)?;

        let swp = pcm.sw_params_current()?;
        swp.set_start_threshold(hwp.get_buffer_size()? - hwp.get_period_size()?)?;
        pcm.sw_params(&swp)?;
    }

    Ok((pcm, period_size))
}

impl Open for AlsaSink {
    fn open(device: Option<String>) -> AlsaSink {
        info!("Using alsa sink");

        let name = match device.as_ref().map(AsRef::as_ref) {
            Some("?") => {
                println!("Listing available alsa outputs");
                list_outputs();
                exit(0)
            }
            Some(device) => device,
            None => "default",
        }
        .to_string();

        AlsaSink {
            pcm: None,
            device: name,
            buffer: vec![],
        }
    }
}

impl Sink for AlsaSink {
    fn start(&mut self) -> io::Result<()> {
        if self.pcm.is_none() {
            let pcm = open_device(&self.device);
            match pcm {
                Ok((p, period_size)) => {
                    self.pcm = Some(p);
                    // Create a buffer for all samples for a full period
                    self.buffer = Vec::with_capacity((period_size * 2) as usize);
                }
                Err(e) => {
                    error!("Alsa error PCM open {}", e);
                    return Err(io::Error::new(
                        io::ErrorKind::Other,
                        "Alsa error: PCM open failed",
                    ));
                }
            }
        }

        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        {
            let pcm = self.pcm.as_mut().unwrap();
            // Write any leftover data in the period buffer
            // before draining the actual buffer
            let io = pcm.io_i16().unwrap();
            match io.writei(&self.buffer[..]) {
                Ok(_) => (),
                Err(err) => pcm.try_recover(err, false).unwrap(),
            }
            pcm.drain().unwrap();
        }
        self.pcm = None;
        Ok(())
    }

    fn write(&mut self, data: &[i16]) -> io::Result<()> {
        let mut processed_data = 0;
        while processed_data < data.len() {
            let data_to_buffer = min(
                self.buffer.capacity() - self.buffer.len(),
                data.len() - processed_data,
            );
            self.buffer
                .extend_from_slice(&data[processed_data..processed_data + data_to_buffer]);
            processed_data += data_to_buffer;
            if self.buffer.len() == self.buffer.capacity() {
                let pcm = self.pcm.as_mut().unwrap();
                let io = pcm.io_i16().unwrap();
                match io.writei(&self.buffer) {
                    Ok(_) => (),
                    Err(err) => pcm.try_recover(err, false).unwrap(),
                }
                self.buffer.clear();
            }
        }

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