<commit_before>use crate::{KeyError, ReadOnly, StringKey};
use std::convert::TryInto;
use std::ffi::{CStr, CString};

pub trait ReadableKey {
    type Value;

    fn as_ref(&self) -> &elektra_sys::Key;
    fn get_value(&self) -> Self::Value;

    /// Construct a new key from a raw key pointer
    fn from_ptr(ptr: *mut elektra_sys::Key) -> Self
    where
        Self: Sized;

    // TODO keyCopy?

    // keyname methods

    /// Return the name of the key as a borrowed slice.
    /// # Panics
    /// Panics if the underlying string cannot be converted to UTF-8.
    fn get_name(&self) -> &str {
        let c_str = unsafe { CStr::from_ptr(elektra_sys::keyName(self.as_ref())) };
        c_str.to_str().unwrap()
    }

    /// Return the basename of the key as a borrowed slice.
    /// # Panics
    /// Panics if the underlying string cannot be converted to UTF-8.
    fn get_basename(&self) -> &str {
        let c_str = unsafe { CStr::from_ptr(elektra_sys::keyBaseName(self.as_ref())) };
        c_str.to_str().unwrap()
    }

    /// Calculates number of bytes needed to store basename of key.
    fn get_basename_size(&self) -> isize {
        unsafe { elektra_sys::keyGetBaseNameSize(self.as_ref()) }
    }

    /// Bytes needed to store the key name including user domain and ending NULL.
    fn get_fullname_size(&self) -> usize {
        unsafe {
            elektra_sys::keyGetFullNameSize(self.as_ref())
                .try_into()
                .unwrap()
        }
    }

    /// Get key full name, including the user domain name.
    /// # Panics
    /// Panics if the underlying c_string contains interior nul bytes
    /// or cannot be converted to UTF-8
    fn get_fullname(&self) -> String {
        let mut vec: Vec<u8> = Vec::with_capacity(self.get_fullname_size());

        let ret_val = unsafe {
            elektra_sys::keyGetFullName(
                self.as_ref(),
                vec.as_mut_ptr() as *mut std::os::raw::c_char,
                vec.capacity(),
            )
        };
        unsafe { vec.set_len(ret_val.try_into().unwrap()) };
        CStr::from_bytes_with_nul(&vec)
            .unwrap()
            .to_str()
            .unwrap()
            .to_owned()
    }

    fn get_namespace(&self) -> u32 {
        unsafe { elektra_sys::keyGetNamespace(self.as_ref()) as u32 }
    }

    /// Determines if the key is in the spec namespace
    fn is_spec(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_SPEC
    }

    /// Determines if the key is in the dir namespace
    fn is_dir(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_DIR
    }

    /// Determines if the key is in the proc namespace
    fn is_proc(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_PROC
    }

    /// Determines if the key is in the user namespace
    fn is_user(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_USER
    }

    /// Determines if the key is in the system namespace
    fn is_system(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_SYSTEM
    }

    /// Determines if the key is in the dir namespace
    fn is_cascading(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_CASCADING
    }

    // keyvalue methds

    // Omitted keyValue() due to return value of void pointer, which cannot be used safely in Rust

    /// Returns the number of bytes needed to store the key value, including the
    /// NULL terminator.
    fn get_value_size(&self) -> usize {
        let ret_val = unsafe { elektra_sys::keyGetValueSize(self.as_ref()) };
        // keyGetValueSize returns -1 on null pointers, but we can be sure self.ptr is valid
        // so this conversion is safe
        ret_val.try_into().unwrap()
    }

    // keytest methods

    /// Returns true if the key has a binary value.
    fn is_binary(&self) -> bool {
        unsafe { elektra_sys::keyIsBinary(self.as_ref()) == 1 }
    }

    /// Returns true if the key has a string value.
    fn is_string(&self) -> bool {
        unsafe { elektra_sys::keyIsString(self.as_ref()) == 1 }
    }

    /// Returns true if other is below self
    /// # Examples
    /// ```
    /// use elektra::{StringKey,WriteableKey,ReadableKey};
    /// let key = StringKey::new("user/sw/app").unwrap();
    /// let key2 = StringKey::new("user/sw/app/folder/key").unwrap();
    /// assert!(key2.is_below(&key));
    /// ```
    fn is_below(&self, other: &Self) -> bool
    where
        Self: Sized,
    {
        unsafe { elektra_sys::keyIsBelow(other.as_ref(), self.as_ref()) == 1 }
    }

    /// Returns true if other is *directly* below self
    /// # Examples
    /// ```
    /// use elektra::{StringKey,WriteableKey,ReadableKey};
    /// let key = StringKey::new("user/sw/app").unwrap();
    /// let key2 = StringKey::new("user/sw/app/key").unwrap();
    /// assert!(key2.is_direct_below(&key));
    /// ```
    fn is_direct_below(&self, other: &Self) -> bool
    where
        Self: Sized,
    {
        unsafe { elektra_sys::keyIsDirectBelow(other.as_ref(), self.as_ref()) == 1 }
    }

    /// Returns true if the key is inactive.
    /// In Elektra terminology a hierarchy of keys is inactive if the
    /// rootkey's basename starts with '.'. So a key is also inactive
    /// if it is below an inactive key. For example, `user/key/.hidden`
    /// is inactive and so is `user/.hidden/below`.
    fn is_inactive(&self) -> bool {
        unsafe { elektra_sys::keyIsInactive(self.as_ref()) == 1 }
    }

    /// Returns the metadata with the given metaname
    fn get_meta(&self, metaname: &str) -> Result<ReadOnly<StringKey<'_>>, KeyError>
    where
        Self: Sized,
    {
        let cstr = CString::new(metaname).unwrap();
        let key_ptr = unsafe { elektra_sys::keyGetMeta(self.as_ref(), cstr.as_ptr()) };
        if key_ptr.is_null() {
            Err(KeyError::NotFound)
        } else {
            let key:ReadOnly<StringKey<'_>> = ReadOnly::from_ptr(key_ptr as *mut elektra_sys::Key);
            Ok(key)
        }
    }
}
<commit_msg>Rust: Add doctests & test them on the build server<commit_after>use crate::{KeyError, ReadOnly, StringKey};
use std::convert::TryInto;
use std::ffi::{CStr, CString};

pub trait ReadableKey {
    type Value;

    fn as_ref(&self) -> &elektra_sys::Key;
    fn get_value(&self) -> Self::Value;

    /// Construct a new key from a raw key pointer
    fn from_ptr(ptr: *mut elektra_sys::Key) -> Self
    where
        Self: Sized;

    // TODO keyCopy?

    // keyname methods

    /// Return the name of the key as a borrowed slice.
    /// # Panics
    /// Panics if the underlying string cannot be converted to UTF-8.
    fn get_name(&self) -> &str {
        let c_str = unsafe { CStr::from_ptr(elektra_sys::keyName(self.as_ref())) };
        c_str.to_str().unwrap()
    }

    /// Return the basename of the key as a borrowed slice.
    /// # Panics
    /// Panics if the underlying string cannot be converted to UTF-8.
    fn get_basename(&self) -> &str {
        let c_str = unsafe { CStr::from_ptr(elektra_sys::keyBaseName(self.as_ref())) };
        c_str.to_str().unwrap()
    }

    /// Calculates number of bytes needed to store basename of key.
    fn get_basename_size(&self) -> isize {
        unsafe { elektra_sys::keyGetBaseNameSize(self.as_ref()) }
    }

    /// Bytes needed to store the key name including user domain and ending NULL.
    fn get_fullname_size(&self) -> usize {
        unsafe {
            elektra_sys::keyGetFullNameSize(self.as_ref())
                .try_into()
                .unwrap()
        }
    }

    /// Get key full name, including the user domain name.
    /// # Panics
    /// Panics if the underlying c_string contains interior nul bytes
    /// or cannot be converted to UTF-8
    fn get_fullname(&self) -> String {
        let mut vec: Vec<u8> = Vec::with_capacity(self.get_fullname_size());

        let ret_val = unsafe {
            elektra_sys::keyGetFullName(
                self.as_ref(),
                vec.as_mut_ptr() as *mut std::os::raw::c_char,
                vec.capacity(),
            )
        };
        unsafe { vec.set_len(ret_val.try_into().unwrap()) };
        CStr::from_bytes_with_nul(&vec)
            .unwrap()
            .to_str()
            .unwrap()
            .to_owned()
    }

    fn get_namespace(&self) -> u32 {
        unsafe { elektra_sys::keyGetNamespace(self.as_ref()) as u32 }
    }

    /// Determines if the key is in the spec namespace
    fn is_spec(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_SPEC
    }

    /// Determines if the key is in the dir namespace
    fn is_dir(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_DIR
    }

    /// Determines if the key is in the proc namespace
    fn is_proc(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_PROC
    }

    /// Determines if the key is in the user namespace
    fn is_user(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_USER
    }

    /// Determines if the key is in the system namespace
    fn is_system(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_SYSTEM
    }

    /// Determines if the key is in the dir namespace
    fn is_cascading(&self) -> bool {
        self.get_namespace() == elektra_sys::KEY_NS_CASCADING
    }

    // Omitted keyValue() due to return value of void pointer, which cannot be used safely in Rust

    /// Returns the number of bytes needed to store the key's value, including the
    /// NULL terminator.
    ///
    /// # Examples
    /// ```
    /// # use std::error::Error;
    /// # use elektra::{BinaryKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn Error>> {
    /// let mut key = BinaryKey::new("user/sw/app")?;
    /// let binary_content = b"12345".to_vec();
    /// key.set_value(binary_content);
    /// assert_eq!(key.get_value_size(), 5);
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn get_value_size(&self) -> usize {
        let ret_val = unsafe { elektra_sys::keyGetValueSize(self.as_ref()) };
        // keyGetValueSize returns -1 on null pointers, but we can be sure self.ptr is valid
        // so this conversion is safe
        ret_val.try_into().unwrap()
    }

    /// Returns true if the key has a binary value.
    ///
    /// # Notes
    /// Note that this does not return true for a newly created BinaryKey,
    /// but only when actual binary data has been set, due to the underlying
    /// generic Key.
    ///
    /// # Examples
    /// ```
    /// # use std::error::Error;
    /// # use elektra::{BinaryKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn Error>> {
    /// let mut key = BinaryKey::new("user/sw/app")?;
    /// let binary_content = b"0".to_vec();
    /// key.set_value(binary_content);
    /// assert!(key.is_binary());
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn is_binary(&self) -> bool {
        unsafe { elektra_sys::keyIsBinary(self.as_ref()) == 1 }
    }

    /// Returns true if the key has a string value.
    ///
    /// # Examples
    /// ```
    /// # use std::error::Error;
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn Error>> {
    /// let key = StringKey::new("user/sw/app")?;
    /// assert!(key.is_string());
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn is_string(&self) -> bool {
        unsafe { elektra_sys::keyIsString(self.as_ref()) == 1 }
    }

    /// Returns true if other is below self
    ///
    /// # Examples
    /// ```
    /// # use std::error::Error;
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn Error>> {
    /// let key = StringKey::new("user/sw/app")?;
    /// let key2 = StringKey::new("user/sw/app/folder/key")?;
    /// assert!(key2.is_below(&key));
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn is_below(&self, other: &Self) -> bool
    where
        Self: Sized,
    {
        unsafe { elektra_sys::keyIsBelow(other.as_ref(), self.as_ref()) == 1 }
    }

    /// Returns true if other is *directly* below self
    ///
    /// # Examples
    /// ```
    /// # use std::error::Error;
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn Error>> {
    /// let key = StringKey::new("user/sw/app")?;
    /// let key2 = StringKey::new("user/sw/app/key")?;
    /// assert!(key2.is_direct_below(&key));
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn is_direct_below(&self, other: &Self) -> bool
    where
        Self: Sized,
    {
        unsafe { elektra_sys::keyIsDirectBelow(other.as_ref(), self.as_ref()) == 1 }
    }

    /// Returns true if the key is inactive.
    ///
    /// In Elektra terminology a hierarchy of keys is inactive if the
    /// rootkey's basename starts with '.'. So a key is also inactive
    /// if it is below an inactive key.
    /// # Examples
    /// ```
    /// # use std::error::Error;
    /// # use elektra::{StringKey,WriteableKey,ReadableKey};
    /// # fn main() -> Result<(), Box<dyn Error>> {
    /// let key = StringKey::new("user/key/.hidden")?;
    /// assert!(key.is_inactive());
    /// #
    /// #     Ok(())
    /// # }
    /// ```
    fn is_inactive(&self) -> bool {
        unsafe { elektra_sys::keyIsInactive(self.as_ref()) == 1 }
    }

    /// Returns the metadata with the given metaname
    fn get_meta(&self, metaname: &str) -> Result<ReadOnly<StringKey<'_>>, KeyError>
    where
        Self: Sized,
    {
        let cstr = CString::new(metaname).unwrap();
        let key_ptr = unsafe { elektra_sys::keyGetMeta(self.as_ref(), cstr.as_ptr()) };
        if key_ptr.is_null() {
            Err(KeyError::NotFound)
        } else {
            let key: ReadOnly<StringKey<'_>> = ReadOnly::from_ptr(key_ptr as *mut elektra_sys::Key);
            Ok(key)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// 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.

//! Support for sending and receiving data link layer packets.

extern crate ipnetwork;
extern crate libc;
extern crate pnet_base;
extern crate pnet_sys;

use std::io;
use std::option::Option;
use std::time::Duration;

use ipnetwork::IpNetwork;

pub use pnet_base::{MacAddr, ParseMacAddrErr};

mod bindings;

#[cfg(windows)]
#[path = "winpcap.rs"]
mod backend;

#[cfg(windows)]
pub mod winpcap;

#[cfg(all(not(feature = "netmap"),
          any(target_os = "linux",
              target_os = "android"
             )
         )
      )]
#[path = "linux.rs"]
mod backend;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;

#[cfg(all(not(feature = "netmap"),
          any(target_os = "freebsd",
              target_os = "openbsd",
              target_os = "macos")
             )
     )]
#[path = "bpf.rs"]
mod backend;

#[cfg(any(target_os = "freebsd", target_os = "macos"))]
pub mod bpf;

#[cfg(feature = "netmap")]
#[path = "netmap.rs"]
mod backend;

#[cfg(feature = "netmap")]
pub mod netmap;

#[cfg(feature = "pcap")]
pub mod pcap;

pub mod dummy;

/// Type alias for an `EtherType`.
pub type EtherType = u16;

/// Type of data link channel to present (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ChannelType {
    /// Send and receive layer 2 packets directly, including headers.
    Layer2,
    /// Send and receive "cooked" packets - send and receive network layer packets.
    Layer3(EtherType),
}

/// A channel for sending and receiving at the data link layer.
///
/// NOTE: It is important to always include a catch-all variant in match statements using this
/// enum, since new variants may be added. For example:
///
/// ```ignore
/// match some_channel {
///     Ethernet(tx, rx) => { /* Handle Ethernet packets */ },
///     _ => panic!("Unhandled channel type")
/// }
/// ```
pub enum Channel {
    /// A datalink channel which sends and receives Ethernet packets.
    Ethernet(Box<dyn DataLinkSender>, Box<dyn DataLinkReceiver>),

    /// This variant should never be used.
    ///
    /// Including it allows new variants to be added to `Channel` without breaking existing code.
    PleaseIncludeACatchAllVariantWhenMatchingOnThisEnum,
}

/// Socket fanout type (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FanoutType {
    HASH,
    LB,
    CPU,
    ROLLOVER,
    RND,
    QM,
    CBPF,
    EBPF,
}

/// Fanout settings (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FanoutOption {
    pub group_id: u16,
    pub fanout_type: FanoutType,
    pub defrag: bool,
    pub rollover: bool,
}

/// A generic configuration type, encapsulating all options supported by each backend.
///
/// Each option should be treated as a hint - each backend is free to ignore any and all
/// options which don't apply to it.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when writing packets. Defaults to 4096.
    pub write_buffer_size: usize,

    /// The size of buffer to use when reading packets. Defaults to 4096.
    pub read_buffer_size: usize,

    /// Linux/BPF/Netmap only: The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,

    /// Linux/BPF/Netmap only: The write timeout. Defaults to None.
    pub write_timeout: Option<Duration>,

    /// Linux only: Specifies whether to read packets at the datalink layer or network layer.
    /// Defaults to Layer2
    pub channel_type: ChannelType,

    /// BPF/OS X only: The number of /dev/bpf* file descriptors to attempt before failing. Defaults
    /// to: 1000.
    pub bpf_fd_attempts: usize,

    pub linux_fanout: Option<FanoutOption>,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            write_buffer_size: 4096,
            read_buffer_size: 4096,
            read_timeout: None,
            write_timeout: None,
            channel_type: ChannelType::Layer2,
            bpf_fd_attempts: 1000,
            linux_fanout: None,
        }
    }
}

/// Create a new datalink channel for sending and receiving data.
///
/// This allows for sending and receiving packets at the data link layer.
///
/// A list of network interfaces can be retrieved using datalink::interfaces().
///
/// The configuration serves as a hint to the backend - some or all of it may be used or ignored,
/// depending on which backend is used.
///
/// When matching on the returned channel, make sure to include a catch-all so that code doesn't
/// break when new channel types are added.
#[inline]
pub fn channel(network_interface: &NetworkInterface, configuration: Config) -> io::Result<Channel> {
    backend::channel(network_interface, (&configuration).into())
}

/// Trait to enable sending `$packet` packets.
pub trait DataLinkSender: Send {
    /// Create and send a number of packets.
    ///
    /// This will call `func` `num_packets` times. The function will be provided with a
    /// mutable packet to manipulate, which will then be sent. This allows packets to be
    /// built in-place, avoiding the copy required for `send`. If there is not sufficient
    /// capacity in the buffer, None will be returned.
    #[inline]
    fn build_and_send(
        &mut self,
        num_packets: usize,
        packet_size: usize,
        func: &mut dyn FnMut(&mut [u8]),
    ) -> Option<io::Result<()>>;

    /// Send a packet.
    ///
    /// This may require an additional copy compared to `build_and_send`, depending on the
    /// operating system being used. The second parameter is currently ignored, however
    /// `None` should be passed.
    #[inline]
    fn send_to(&mut self, packet: &[u8], dst: Option<NetworkInterface>) -> Option<io::Result<()>>;
}

/// Structure for receiving packets at the data link layer. Should be constructed using
/// `datalink_channel()`.
pub trait DataLinkReceiver: Send {
    #[inline]
    /// Get the next ethernet frame in the channel.
    fn next(&mut self) -> io::Result<&[u8]>;
}

/// Represents a network interface and its associated addresses.
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct NetworkInterface {
    /// The name of the interface.
    pub name: String,
    /// The interface index (operating system specific).
    pub index: u32,
    /// A MAC address for the interface.
    pub mac: Option<MacAddr>,
    /// IP addresses and netmasks for the interface.
    pub ips: Vec<IpNetwork>,
    /// Operating system specific flags for the interface.
    pub flags: u32,
}

impl NetworkInterface {
    /// Retrieve the MAC address associated with the interface.
    pub fn mac_address(&self) -> MacAddr {
        self.mac.unwrap()
    }

    pub fn is_up(&self) -> bool {
        self.flags & (pnet_sys::IFF_UP as u32) != 0
    }
    pub fn is_broadcast(&self) -> bool {
        self.flags & (pnet_sys::IFF_BROADCAST as u32) != 0
    }
    /// Is the interface a loopback interface?
    pub fn is_loopback(&self) -> bool {
        self.flags & (pnet_sys::IFF_LOOPBACK as u32) != 0
    }
    pub fn is_point_to_point(&self) -> bool {
        self.flags & (pnet_sys::IFF_POINTOPOINT as u32) != 0
    }
    pub fn is_multicast(&self) -> bool {
        self.flags & (pnet_sys::IFF_MULTICAST as u32) != 0
    }
}

impl ::std::fmt::Display for NetworkInterface {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        const FLAGS: [&'static str; 5] =
            ["UP", "BROADCAST", "LOOPBACK", "POINTOPOINT", "MULTICAST"];
        let flags = if self.flags > 0 {
            let rets = [
                self.is_up(),
                self.is_broadcast(),
                self.is_loopback(),
                self.is_point_to_point(),
                self.is_multicast(),
            ];
            format!(
                "{:X}<{}>",
                self.flags,
                rets.iter()
                    .zip(FLAGS.iter())
                    .filter(|&(ret, _)| ret == &true)
                    .map(|(_, name)| name.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            )
        } else {
            format!("{:X}", self.flags)
        };

        let mac = self.mac
            .map(|mac| mac.to_string())
            .unwrap_or("N/A".to_owned());
        let ips = if self.ips.len() > 0 {
            format!(
                "\n{}",
                self.ips
                    .iter()
                    .map(|ip| {
                        if ip.is_ipv4() {
                            format!("       inet: {}", ip)
                        } else {
                            format!("      inet6: {}", ip)
                        }
                    })
                    .collect::<Vec<String>>()
                    .join("\n")
            )
        } else {
            "".to_string()
        };

        write!(
            f,
            "{}: flags={}
      index: {}
      ether: {}{}",
            self.name, flags, self.index, mac, ips
        )
    }
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
    backend::interfaces()
}
<commit_msg>Add docs on how to get the default network interface for packet sniffing.<commit_after>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// 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.

//! Support for sending and receiving data link layer packets.

extern crate ipnetwork;
extern crate libc;
extern crate pnet_base;
extern crate pnet_sys;

use std::io;
use std::option::Option;
use std::time::Duration;

use ipnetwork::IpNetwork;

pub use pnet_base::{MacAddr, ParseMacAddrErr};

mod bindings;

#[cfg(windows)]
#[path = "winpcap.rs"]
mod backend;

#[cfg(windows)]
pub mod winpcap;

#[cfg(all(not(feature = "netmap"),
          any(target_os = "linux",
              target_os = "android"
             )
         )
      )]
#[path = "linux.rs"]
mod backend;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;

#[cfg(all(not(feature = "netmap"),
          any(target_os = "freebsd",
              target_os = "openbsd",
              target_os = "macos")
             )
     )]
#[path = "bpf.rs"]
mod backend;

#[cfg(any(target_os = "freebsd", target_os = "macos"))]
pub mod bpf;

#[cfg(feature = "netmap")]
#[path = "netmap.rs"]
mod backend;

#[cfg(feature = "netmap")]
pub mod netmap;

#[cfg(feature = "pcap")]
pub mod pcap;

pub mod dummy;

/// Type alias for an `EtherType`.
pub type EtherType = u16;

/// Type of data link channel to present (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ChannelType {
    /// Send and receive layer 2 packets directly, including headers.
    Layer2,
    /// Send and receive "cooked" packets - send and receive network layer packets.
    Layer3(EtherType),
}

/// A channel for sending and receiving at the data link layer.
///
/// NOTE: It is important to always include a catch-all variant in match statements using this
/// enum, since new variants may be added. For example:
///
/// ```ignore
/// match some_channel {
///     Ethernet(tx, rx) => { /* Handle Ethernet packets */ },
///     _ => panic!("Unhandled channel type")
/// }
/// ```
pub enum Channel {
    /// A datalink channel which sends and receives Ethernet packets.
    Ethernet(Box<dyn DataLinkSender>, Box<dyn DataLinkReceiver>),

    /// This variant should never be used.
    ///
    /// Including it allows new variants to be added to `Channel` without breaking existing code.
    PleaseIncludeACatchAllVariantWhenMatchingOnThisEnum,
}

/// Socket fanout type (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FanoutType {
    HASH,
    LB,
    CPU,
    ROLLOVER,
    RND,
    QM,
    CBPF,
    EBPF,
}

/// Fanout settings (Linux only).
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FanoutOption {
    pub group_id: u16,
    pub fanout_type: FanoutType,
    pub defrag: bool,
    pub rollover: bool,
}

/// A generic configuration type, encapsulating all options supported by each backend.
///
/// Each option should be treated as a hint - each backend is free to ignore any and all
/// options which don't apply to it.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when writing packets. Defaults to 4096.
    pub write_buffer_size: usize,

    /// The size of buffer to use when reading packets. Defaults to 4096.
    pub read_buffer_size: usize,

    /// Linux/BPF/Netmap only: The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,

    /// Linux/BPF/Netmap only: The write timeout. Defaults to None.
    pub write_timeout: Option<Duration>,

    /// Linux only: Specifies whether to read packets at the datalink layer or network layer.
    /// Defaults to Layer2
    pub channel_type: ChannelType,

    /// BPF/OS X only: The number of /dev/bpf* file descriptors to attempt before failing. Defaults
    /// to: 1000.
    pub bpf_fd_attempts: usize,

    pub linux_fanout: Option<FanoutOption>,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            write_buffer_size: 4096,
            read_buffer_size: 4096,
            read_timeout: None,
            write_timeout: None,
            channel_type: ChannelType::Layer2,
            bpf_fd_attempts: 1000,
            linux_fanout: None,
        }
    }
}

/// Create a new datalink channel for sending and receiving data.
///
/// This allows for sending and receiving packets at the data link layer.
///
/// A list of network interfaces can be retrieved using datalink::interfaces().
///
/// The configuration serves as a hint to the backend - some or all of it may be used or ignored,
/// depending on which backend is used.
///
/// When matching on the returned channel, make sure to include a catch-all so that code doesn't
/// break when new channel types are added.
#[inline]
pub fn channel(network_interface: &NetworkInterface, configuration: Config) -> io::Result<Channel> {
    backend::channel(network_interface, (&configuration).into())
}

/// Trait to enable sending `$packet` packets.
pub trait DataLinkSender: Send {
    /// Create and send a number of packets.
    ///
    /// This will call `func` `num_packets` times. The function will be provided with a
    /// mutable packet to manipulate, which will then be sent. This allows packets to be
    /// built in-place, avoiding the copy required for `send`. If there is not sufficient
    /// capacity in the buffer, None will be returned.
    #[inline]
    fn build_and_send(
        &mut self,
        num_packets: usize,
        packet_size: usize,
        func: &mut dyn FnMut(&mut [u8]),
    ) -> Option<io::Result<()>>;

    /// Send a packet.
    ///
    /// This may require an additional copy compared to `build_and_send`, depending on the
    /// operating system being used. The second parameter is currently ignored, however
    /// `None` should be passed.
    #[inline]
    fn send_to(&mut self, packet: &[u8], dst: Option<NetworkInterface>) -> Option<io::Result<()>>;
}

/// Structure for receiving packets at the data link layer. Should be constructed using
/// `datalink_channel()`.
pub trait DataLinkReceiver: Send {
    #[inline]
    /// Get the next ethernet frame in the channel.
    fn next(&mut self) -> io::Result<&[u8]>;
}

/// Represents a network interface and its associated addresses.
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct NetworkInterface {
    /// The name of the interface.
    pub name: String,
    /// The interface index (operating system specific).
    pub index: u32,
    /// A MAC address for the interface.
    pub mac: Option<MacAddr>,
    /// IP addresses and netmasks for the interface.
    pub ips: Vec<IpNetwork>,
    /// Operating system specific flags for the interface.
    pub flags: u32,
}

impl NetworkInterface {
    /// Retrieve the MAC address associated with the interface.
    pub fn mac_address(&self) -> MacAddr {
        self.mac.unwrap()
    }

    pub fn is_up(&self) -> bool {
        self.flags & (pnet_sys::IFF_UP as u32) != 0
    }
    pub fn is_broadcast(&self) -> bool {
        self.flags & (pnet_sys::IFF_BROADCAST as u32) != 0
    }
    /// Is the interface a loopback interface?
    pub fn is_loopback(&self) -> bool {
        self.flags & (pnet_sys::IFF_LOOPBACK as u32) != 0
    }
    pub fn is_point_to_point(&self) -> bool {
        self.flags & (pnet_sys::IFF_POINTOPOINT as u32) != 0
    }
    pub fn is_multicast(&self) -> bool {
        self.flags & (pnet_sys::IFF_MULTICAST as u32) != 0
    }
}

impl ::std::fmt::Display for NetworkInterface {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        const FLAGS: [&'static str; 5] =
            ["UP", "BROADCAST", "LOOPBACK", "POINTOPOINT", "MULTICAST"];
        let flags = if self.flags > 0 {
            let rets = [
                self.is_up(),
                self.is_broadcast(),
                self.is_loopback(),
                self.is_point_to_point(),
                self.is_multicast(),
            ];
            format!(
                "{:X}<{}>",
                self.flags,
                rets.iter()
                    .zip(FLAGS.iter())
                    .filter(|&(ret, _)| ret == &true)
                    .map(|(_, name)| name.to_string())
                    .collect::<Vec<String>>()
                    .join(",")
            )
        } else {
            format!("{:X}", self.flags)
        };

        let mac = self.mac
            .map(|mac| mac.to_string())
            .unwrap_or("N/A".to_owned());
        let ips = if self.ips.len() > 0 {
            format!(
                "\n{}",
                self.ips
                    .iter()
                    .map(|ip| {
                        if ip.is_ipv4() {
                            format!("       inet: {}", ip)
                        } else {
                            format!("      inet6: {}", ip)
                        }
                    })
                    .collect::<Vec<String>>()
                    .join("\n")
            )
        } else {
            "".to_string()
        };

        write!(
            f,
            "{}: flags={}
      index: {}
      ether: {}{}",
            self.name, flags, self.index, mac, ips
        )
    }
}

/// Get a list of available network interfaces for the current machine.
///
/// If you need the default network interface, you can choose the first
/// one that is up, not loopback and has an IP. This is not guaranteed to
/// work on each system but should work for basic packet sniffing:
///
/// ```
/// use pnet_datalink::interfaces;
///
/// // Get a vector with all network interfaces found
/// let all_interfaces = interfaces();
///
/// // Search for the default interface - the one that is
/// // up, not loopback and has an IP.
/// let default_interface = all_interfaces
///     .iter()
///     .filter(|e| e.is_up() && !e.is_loopback() && e.ips.len() > 0)
///     .next();
///
/// match default_interface {
///     Some(interface) => println!("Found default interface with [{}].", interface.name),
///     None => println!("Error while finding the default interface."),
/// }
///
/// ```
///
pub fn interfaces() -> Vec<NetworkInterface> {
    backend::interfaces()
}
<|endoftext|>


<commit_before>use std::io::Write;
use std::option::Option;
use std::os::unix::process::CommandExt;
use std::process::Command;

const SYSTEMD_RUN: &'static str = "/usr/bin/systemd-run";
const FIREJAIL: &'static str = "/usr/bin/firejail";

const FIREFOX: &'static str = "/usr/bin/firefox";
const FIREFOX_PROFILE: &'static str = "/etc/firejail/firefox.profile";
const FIREFOX_MOZILLA_SFW_PROFILE: &'static str = "default-1473025815439";

const FIREFOX_MEMORY_HIGH: i32 = 4420;
const FIREFOX_MEMORY_MAX: i32 = 5200;

const KEIRA: &'static str = "/home/kalvin/Downloads/.firefox-nightly/firefox";
const KEIRA_PROFILE: &'static str = "/etc/firejail/firefox-nightly.profile";

const MPV: &'static str = "/usr/bin/mpv";
const MPV_PROFILE: &'static str = "/etc/firejail/mpv.profile";

const TERM: &'static str = "/usr/bin/alacritty";
const TERM_PROFILE: &'static str = "/etc/firejail/x-terminal-emulator.profile";

const THUNDERBIRD: &'static str = "/home/kalvin/Downloads/.thunderbird-beta/thunderbird";
const THUNDERBIRD_PROFILE: &'static str = "/etc/firejail/thunderbird-beta.profile";

const QUODLIBET_FIFO: &'static str = "/home/kalvin/.config/quodlibet/control";

#[derive(Debug)]
struct CgroupedFirejailedCommandOptions<'a> {
    bin_path: &'a str,
    memory_high: Option<i32>,
    memory_max: Option<i32>,
    firejail_profile: Option<&'a str>,
    implicit_extra_args: Option<&'a [&'a str]>,
    argv_remainder: Vec<String>,
}

fn cgrouped_firejail_command(options: CgroupedFirejailedCommandOptions) -> Command {
    let mut command = Command::new(SYSTEMD_RUN);
    command.args(&["--user", "--scope"]);
    command.args(&["-p", "MemorySwapMax=0"]);

    if let Some(memory_high) = options.memory_high {
        let memory_high_owned_string = format!("MemoryHigh={}M", memory_high);
        command.args(&["-p", memory_high_owned_string.as_str()]);
    }
    if let Some(memory_max) = options.memory_max {
        let memory_max_owned_string = format!("MemoryMax={}M", memory_max);
        command.args(&["-p", memory_max_owned_string.as_str()]);
    }

    if let Some(firejail_profile) = options.firejail_profile {
        command.arg(FIREJAIL);
        command.arg("--ignore=seccomp");
        command.arg(format!("--profile={}", firejail_profile));
    }

    command.arg(options.bin_path);

    if let Some(implicit_extra_args) = options.implicit_extra_args {
        command.args(implicit_extra_args);
    }

    command.args(options.argv_remainder);

    command
}

fn simple_firejail_command(target: &str, args: Vec<String>) -> Command {
    let mut command = Command::new(FIREJAIL);

    command.arg(format!("--profile=/etc/firejail/{}.profile", target));
    command.arg(format!("/usr/bin/{}", target));
    command.args(args);

    command
}

fn quodlibet_command(args: Vec<String>) {
    let mut file = std::fs::OpenOptions::new()
        .append(true)
        .open(QUODLIBET_FIFO)
        .unwrap();
    file.write_all(match args[0].as_str() {
        "pp" => "play-pause".as_bytes(),
        "sa" => "stop-after 1".as_bytes(),
        _ => panic!(
            "bad argument for quodlibet command: ``{}''",
            args[0].as_str()
        ),
    })
    .unwrap();
}

fn init_command() -> Option<Command> {
    let mut args_less_the_first = std::env::args();
    // Pops the name of this executable.
    args_less_the_first.next();

    // Pops the name of the unner target.
    let target = args_less_the_first.next().unwrap();

    // Copies and collects the remaining argv.
    let args: Vec<String> = args_less_the_first.collect();

    match target.as_str() {
        "ff" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: FIREFOX,
                    memory_high: Some(FIREFOX_MEMORY_HIGH),
                    memory_max: Some(FIREFOX_MEMORY_MAX),
                    firejail_profile: Some(FIREFOX_PROFILE),
                    implicit_extra_args: Some(&["-P", FIREFOX_MOZILLA_SFW_PROFILE]),
                    argv_remainder: args,
                },
            ));
        }
        "keira" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: KEIRA,
                    memory_high: Some(FIREFOX_MEMORY_HIGH),
                    memory_max: Some(FIREFOX_MEMORY_MAX),
                    firejail_profile: Some(KEIRA_PROFILE),
                    implicit_extra_args: Some(&["-P", "nightly"]),
                    argv_remainder: args,
                },
            ));
        }
        "npv" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: MPV,
                    memory_high: None,
                    memory_max: None,
                    firejail_profile: Some(MPV_PROFILE),
                    implicit_extra_args: Some(&["--pulse-buffer=13"]),
                    argv_remainder: args,
                },
            ));
        }
        "q" => {
            quodlibet_command(args);
            return None;
        }
        "read" => {
            let formatted_args: Vec<String> = vec![format!("about:reader?url={}", args[0])];
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: FIREFOX,
                    memory_high: Some(FIREFOX_MEMORY_HIGH),
                    memory_max: Some(FIREFOX_MEMORY_MAX),
                    firejail_profile: Some(FIREFOX_PROFILE),
                    implicit_extra_args: Some(&["-P", FIREFOX_MOZILLA_SFW_PROFILE]),
                    argv_remainder: formatted_args,
                },
            ));
        }
        "t" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: TERM,
                    memory_high: None,
                    memory_max: None,
                    firejail_profile: Some(TERM_PROFILE),
                    implicit_extra_args: None,
                    argv_remainder: args,
                },
            ));
        }
        "tbb" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: THUNDERBIRD,
                    memory_high: None,
                    memory_max: None,
                    firejail_profile: Some(THUNDERBIRD_PROFILE),
                    implicit_extra_args: None,
                    argv_remainder: args,
                },
            ));
        }
        "vlk" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: "/usr/bin/vlc",
                    memory_high: None,
                    memory_max: None,
                    firejail_profile: Some("/etc/firejail/vlc.profile"),
                    implicit_extra_args: None,
                    argv_remainder: args,
                },
            ));
        }
        "e" => return Some(simple_firejail_command("evince", args)),
        _ => panic!("no handler for ``{}''", target),
    }
}

fn main() {
    if let Some(mut command) = init_command() {
        println!("{:#?}", command);
        command.exec();
    }
}
<commit_msg>unner: use "--play-and-exit"<commit_after>use std::io::Write;
use std::option::Option;
use std::os::unix::process::CommandExt;
use std::process::Command;

const SYSTEMD_RUN: &'static str = "/usr/bin/systemd-run";
const FIREJAIL: &'static str = "/usr/bin/firejail";

const FIREFOX: &'static str = "/usr/bin/firefox";
const FIREFOX_PROFILE: &'static str = "/etc/firejail/firefox.profile";
const FIREFOX_MOZILLA_SFW_PROFILE: &'static str = "default-1473025815439";

const FIREFOX_MEMORY_HIGH: i32 = 4420;
const FIREFOX_MEMORY_MAX: i32 = 5200;

const KEIRA: &'static str = "/home/kalvin/Downloads/.firefox-nightly/firefox";
const KEIRA_PROFILE: &'static str = "/etc/firejail/firefox-nightly.profile";

const MPV: &'static str = "/usr/bin/mpv";
const MPV_PROFILE: &'static str = "/etc/firejail/mpv.profile";

const TERM: &'static str = "/usr/bin/alacritty";
const TERM_PROFILE: &'static str = "/etc/firejail/x-terminal-emulator.profile";

const THUNDERBIRD: &'static str = "/home/kalvin/Downloads/.thunderbird-beta/thunderbird";
const THUNDERBIRD_PROFILE: &'static str = "/etc/firejail/thunderbird-beta.profile";

const QUODLIBET_FIFO: &'static str = "/home/kalvin/.config/quodlibet/control";

#[derive(Debug)]
struct CgroupedFirejailedCommandOptions<'a> {
    bin_path: &'a str,
    memory_high: Option<i32>,
    memory_max: Option<i32>,
    firejail_profile: Option<&'a str>,
    implicit_extra_args: Option<&'a [&'a str]>,
    argv_remainder: Vec<String>,
}

fn cgrouped_firejail_command(options: CgroupedFirejailedCommandOptions) -> Command {
    let mut command = Command::new(SYSTEMD_RUN);
    command.args(&["--user", "--scope"]);
    command.args(&["-p", "MemorySwapMax=0"]);

    if let Some(memory_high) = options.memory_high {
        let memory_high_owned_string = format!("MemoryHigh={}M", memory_high);
        command.args(&["-p", memory_high_owned_string.as_str()]);
    }
    if let Some(memory_max) = options.memory_max {
        let memory_max_owned_string = format!("MemoryMax={}M", memory_max);
        command.args(&["-p", memory_max_owned_string.as_str()]);
    }

    if let Some(firejail_profile) = options.firejail_profile {
        command.arg(FIREJAIL);
        command.arg("--ignore=seccomp");
        command.arg(format!("--profile={}", firejail_profile));
    }

    command.arg(options.bin_path);

    if let Some(implicit_extra_args) = options.implicit_extra_args {
        command.args(implicit_extra_args);
    }

    command.args(options.argv_remainder);

    command
}

fn simple_firejail_command(target: &str, args: Vec<String>) -> Command {
    let mut command = Command::new(FIREJAIL);

    command.arg(format!("--profile=/etc/firejail/{}.profile", target));
    command.arg(format!("/usr/bin/{}", target));
    command.args(args);

    command
}

fn quodlibet_command(args: Vec<String>) {
    let mut file = std::fs::OpenOptions::new()
        .append(true)
        .open(QUODLIBET_FIFO)
        .unwrap();
    file.write_all(match args[0].as_str() {
        "pp" => "play-pause".as_bytes(),
        "sa" => "stop-after 1".as_bytes(),
        _ => panic!(
            "bad argument for quodlibet command: ``{}''",
            args[0].as_str()
        ),
    })
    .unwrap();
}

fn init_command() -> Option<Command> {
    let mut args_less_the_first = std::env::args();
    // Pops the name of this executable.
    args_less_the_first.next();

    // Pops the name of the unner target.
    let target = args_less_the_first.next().unwrap();

    // Copies and collects the remaining argv.
    let args: Vec<String> = args_less_the_first.collect();

    match target.as_str() {
        "ff" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: FIREFOX,
                    memory_high: Some(FIREFOX_MEMORY_HIGH),
                    memory_max: Some(FIREFOX_MEMORY_MAX),
                    firejail_profile: Some(FIREFOX_PROFILE),
                    implicit_extra_args: Some(&["-P", FIREFOX_MOZILLA_SFW_PROFILE]),
                    argv_remainder: args,
                },
            ));
        }
        "keira" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: KEIRA,
                    memory_high: Some(FIREFOX_MEMORY_HIGH),
                    memory_max: Some(FIREFOX_MEMORY_MAX),
                    firejail_profile: Some(KEIRA_PROFILE),
                    implicit_extra_args: Some(&["-P", "nightly"]),
                    argv_remainder: args,
                },
            ));
        }
        "npv" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: MPV,
                    memory_high: None,
                    memory_max: None,
                    firejail_profile: Some(MPV_PROFILE),
                    implicit_extra_args: Some(&["--pulse-buffer=13"]),
                    argv_remainder: args,
                },
            ));
        }
        "q" => {
            quodlibet_command(args);
            return None;
        }
        "read" => {
            let formatted_args: Vec<String> = vec![format!("about:reader?url={}", args[0])];
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: FIREFOX,
                    memory_high: Some(FIREFOX_MEMORY_HIGH),
                    memory_max: Some(FIREFOX_MEMORY_MAX),
                    firejail_profile: Some(FIREFOX_PROFILE),
                    implicit_extra_args: Some(&["-P", FIREFOX_MOZILLA_SFW_PROFILE]),
                    argv_remainder: formatted_args,
                },
            ));
        }
        "t" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: TERM,
                    memory_high: None,
                    memory_max: None,
                    firejail_profile: Some(TERM_PROFILE),
                    implicit_extra_args: None,
                    argv_remainder: args,
                },
            ));
        }
        "tbb" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: THUNDERBIRD,
                    memory_high: None,
                    memory_max: None,
                    firejail_profile: Some(THUNDERBIRD_PROFILE),
                    implicit_extra_args: None,
                    argv_remainder: args,
                },
            ));
        }
        "vlk" => {
            return Some(cgrouped_firejail_command(
                CgroupedFirejailedCommandOptions {
                    bin_path: "/usr/bin/vlc",
                    memory_high: None,
                    memory_max: None,
                    firejail_profile: Some("/etc/firejail/vlc.profile"),
                    implicit_extra_args: Some(&["--play-and-exit"]),
                    argv_remainder: args,
                },
            ));
        }
        "e" => return Some(simple_firejail_command("evince", args)),
        _ => panic!("no handler for ``{}''", target),
    }
}

fn main() {
    if let Some(mut command) = init_command() {
        println!("{:#?}", command);
        command.exec();
    }
}
<|endoftext|>


<commit_before>pub mod square_moore;<commit_msg>Coord struct.<commit_after>pub mod square_moore;

/// For some geometry needs.
pub struct Coord {
    x: i32,
    y: i32
}<|endoftext|>


<commit_before>use std::cell::Cell;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::internal::*;
use super::*;
use super::len::*;

// The key optimization for find_first is that a consumer can stop its search if
// some consumer to its left already found a match (and similarly for consumers
// to the right for find_last). To make this work, all consumers need some
// notion of their position in the data relative to other consumers, including
// unindexed consumers that have no built-in notion of position.
//
// To solve this, we assign each consumer a lower and upper bound for an
// imaginary "range" of data that it consumes. The initial consumer starts with
// the range 0..usize::max_value(). The split divides this range in half so that
// one resulting consumer has the range 0..(usize::max_value() / 2), and the
// other has (usize::max_value() / 2)..usize::max_value(). Every subsequent
// split divides the range in half again until it cannot be split anymore
// (i.e. its length is 1), in which case the split returns two consumers with
// the same range. In that case both consumers will continue to consume all
// their data regardless of whether a better match is found, but the reducer
// will still return the correct answer.

#[derive(Copy, Clone)]
enum MatchPosition {
    Leftmost,
    Rightmost,
}

// Returns true if pos1 is a better match than pos2 according to MatchPosition
fn better_position(pos1: usize, pos2: usize, mp: MatchPosition) -> bool {
    match mp {
        MatchPosition::Leftmost => pos1 < pos2,
        MatchPosition::Rightmost => pos1 > pos2,
    }
}

pub fn find_first<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(usize::max_value());
    let consumer = FindConsumer::new(&find_op, MatchPosition::Leftmost, &best_found);
    pi.drive_unindexed(consumer)
}

pub fn find_last<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(0);
    let consumer = FindConsumer::new(&find_op, MatchPosition::Rightmost, &best_found);
    pi.drive_unindexed(consumer)
}

struct FindConsumer<'f, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    lower_bound: Cell<usize>,
    upper_bound: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
}

impl<'f, FIND_OP> FindConsumer<'f, FIND_OP> {
    fn new(find_op: &'f FIND_OP,
           match_position: MatchPosition,
           best_found: &'f AtomicUsize) -> Self {
        FindConsumer {
            find_op: find_op,
            lower_bound: Cell::new(0),
            upper_bound: usize::max_value(),
            match_position: match_position,
            best_found: best_found,
        }
    }

    fn current_index(&self) -> usize {
        match self.match_position {
            MatchPosition::Leftmost => self.lower_bound.get(),
            MatchPosition::Rightmost => self.upper_bound
        }
    }
}

impl<'f, ITEM, FIND_OP> Consumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    type Folder = FindFolder<'f, ITEM, FIND_OP>;
    type Reducer = FindReducer;
    type Result = Option<ITEM>;

    fn cost(&mut self, cost: f64) -> f64 {
        cost * FUNC_ADJUSTMENT
    }

    fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) {
        let dir = self.match_position;
        (self.split_off(),
         self,
         FindReducer { match_position: dir })
    }

    fn into_folder(self) -> Self::Folder {
        FindFolder {
            find_op: self.find_op,
            boundary: self.current_index(),
            match_position: self.match_position,
            best_found: self.best_found,
            item: None,
        }
    }

    fn full(&self) -> bool {
        // can stop consuming if the best found index so far is *strictly*
        // better than anything this consumer will find
        better_position(self.best_found.load(Ordering::Relaxed),
                        self.current_index(),
                        self.match_position)
    }
}

#[test]
fn same_range_first_consumers_return_correct_answer() {
    let find_op = |x: &i32| x % 2 == 0;
    let first_found = AtomicUsize::new(usize::max_value());
    let consumer = FindConsumer::new(&find_op, MatchPosition::Leftmost, &first_found);

    // split until we have an indivisible range
    let bits_in_usize = usize::min_value().count_zeros();
    for i in 0..bits_in_usize {
        consumer.split_off();
    }

    let reducer = consumer.to_reducer();
    // the left and right folders should now have the same range, having
    // exhausted the resolution of usize
    let left_folder = consumer.split_off().into_folder();
    let right_folder = consumer.into_folder();

    let left_folder = left_folder.consume(0).consume(1);
    assert_eq!(left_folder.boundary, right_folder.boundary);
    // expect not full even though a better match has been found because the
    // ranges are the same
    assert!(!right_folder.full());
    let right_folder = right_folder.consume(2).consume(3);
    assert_eq!(reducer.reduce(left_folder.complete(), right_folder.complete()),
               Some(0));
}

#[test]
fn same_range_last_consumers_return_correct_answer() {
    let find_op = |x: &i32| x % 2 == 0;
    let last_found = AtomicUsize::new(0);
    let consumer = FindConsumer::new(&find_op, MatchPosition::Rightmost, &last_found);

    // split until we have an indivisible range
    let bits_in_usize = usize::min_value().count_zeros();
    for i in 0..bits_in_usize {
        consumer.split_off();
    }

    let reducer = consumer.to_reducer();
    // due to the exact calculation in split_off, the very last consumer has a
    // range of width 2, so we use the second-to-last consumer instead to get
    // the same boundary on both folders
    let consumer = consumer.split_off();
    let left_folder = consumer.split_off().into_folder();
    let right_folder = consumer.into_folder();
    let right_folder = right_folder.consume(2).consume(3);
    assert_eq!(left_folder.boundary, right_folder.boundary);
    // expect not full even though a better match has been found because the
    // ranges are the same
    assert!(!left_folder.full());
    let left_folder = left_folder.consume(0).consume(1);
    assert_eq!(reducer.reduce(left_folder.complete(), right_folder.complete()),
               Some(2));
}

impl<'f, ITEM, FIND_OP> UnindexedConsumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    fn split_off(&self) -> Self {
        // Upper bound for one consumer will be lower bound for the other. This
        // overlap is okay, because only one of the bounds will be used for
        // comparing against best_found; the other is kept only to be able to
        // divide the range in half.
        //
        // When the resolution of usize has been exhausted (i.e. when
        // upper_bound = lower_bound), both results of this split will have the
        // same range. When that happens, we lose the ability to tell one
        // consumer to stop working when the other finds a better match, but the
        // reducer ensures that the best answer is still returned (see the test
        // above).
        //
        // This code assumes that the caller of split_off will use the result as
        // the *left* side of this iterator, and the remainder of self as the
        // *right* side.
        let old_lower_bound = self.lower_bound.get();
        let median = old_lower_bound + ((self.upper_bound - old_lower_bound) / 2);
        self.lower_bound.set(median);

        FindConsumer {
            find_op: self.find_op,
            lower_bound: Cell::new(old_lower_bound),
            upper_bound: median,
            match_position: self.match_position,
            best_found: self.best_found,
        }
    }

    fn to_reducer(&self) -> Self::Reducer {
        FindReducer { match_position: self.match_position }
    }
}

struct FindFolder<'f, ITEM, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    boundary: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
    item: Option<ITEM>,
}

impl<'f, FIND_OP: 'f + Fn(&ITEM) -> bool, ITEM> Folder<ITEM> for FindFolder<'f, ITEM, FIND_OP> {
    type Result = Option<ITEM>;

    fn consume(mut self, item: ITEM) -> Self {
        let found_best_in_range = match self.match_position {
            MatchPosition::Leftmost => self.item.is_some(),
            MatchPosition::Rightmost => false,
        };

        if !found_best_in_range && (self.find_op)(&item) {
            // Continuously try to set best_found until we succeed or we
            // discover a better match was already found.
            let mut current = self.best_found.load(Ordering::Relaxed);
            let mut exchange_result = Result::Err(0);
            while !better_position(current, self.boundary, self.match_position) &&
                  exchange_result.is_err() {
                exchange_result = self.best_found.compare_exchange_weak(current,
                                                                        self.boundary,
                                                                        Ordering::Relaxed,
                                                                        Ordering::Relaxed);
                if exchange_result.is_err() {
                    current = self.best_found.load(Ordering::Relaxed);
                }
            }

            if exchange_result.is_ok() {
                self.item = Some(item);
            }
        }
        self
    }

    fn complete(self) -> Self::Result {
        self.item
    }

    fn full(&self) -> bool {
        let found_best_in_range = match self.match_position {
            MatchPosition::Leftmost => self.item.is_some(),
            MatchPosition::Rightmost => false,
        };

        found_best_in_range ||
            better_position(self.best_found.load(Ordering::Relaxed),
                            self.boundary,
                            self.match_position)
    }
}

// These tests requires that a folder be assigned to an iterator with more than
// one element. We can't necessarily determine when that will happen for a given
// input to find_first/find_last, so we test the folder directly here instead.
#[test]
fn find_first_folder_does_not_clobber_first_found() {
    let best_found = AtomicUsize::new(usize::max_value());
    let f = FindFolder {
        find_op: &(|&x: &i32| -> bool { true }),
        boundary: 0,
        match_position: MatchPosition::Leftmost,
        best_found: &best_found,
        item: None,
    };
    let f = f.consume(0_i32).consume(1_i32).consume(2_i32);
    assert!(f.full());
    assert_eq!(f.complete(), Some(0_i32));
}

#[test]
fn find_last_folder_yields_last_match() {
    let best_found = AtomicUsize::new(0);
    let f = FindFolder {
        find_op: &(|&x: &i32| -> bool { true }),
        boundary: 0,
        match_position: MatchPosition::Rightmost,
        best_found: &best_found,
        item: None,
    };
    let f = f.consume(0_i32).consume(1_i32).consume(2_i32);
    assert_eq!(f.complete(), Some(2_i32));
}

struct FindReducer {
    match_position: MatchPosition
}

impl<ITEM> Reducer<Option<ITEM>> for FindReducer {
    fn reduce(self, left: Option<ITEM>, right: Option<ITEM>) -> Option<ITEM> {
        match self.match_position {
            MatchPosition::Leftmost => left.or(right),
            MatchPosition::Rightmost => right.or(left)
        }
    }
}
<commit_msg>Test fullness of find_first/last consumers outside exhausted range<commit_after>use std::cell::Cell;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::internal::*;
use super::*;
use super::len::*;

// The key optimization for find_first is that a consumer can stop its search if
// some consumer to its left already found a match (and similarly for consumers
// to the right for find_last). To make this work, all consumers need some
// notion of their position in the data relative to other consumers, including
// unindexed consumers that have no built-in notion of position.
//
// To solve this, we assign each consumer a lower and upper bound for an
// imaginary "range" of data that it consumes. The initial consumer starts with
// the range 0..usize::max_value(). The split divides this range in half so that
// one resulting consumer has the range 0..(usize::max_value() / 2), and the
// other has (usize::max_value() / 2)..usize::max_value(). Every subsequent
// split divides the range in half again until it cannot be split anymore
// (i.e. its length is 1), in which case the split returns two consumers with
// the same range. In that case both consumers will continue to consume all
// their data regardless of whether a better match is found, but the reducer
// will still return the correct answer.

#[derive(Copy, Clone)]
enum MatchPosition {
    Leftmost,
    Rightmost,
}

// Returns true if pos1 is a better match than pos2 according to MatchPosition
fn better_position(pos1: usize, pos2: usize, mp: MatchPosition) -> bool {
    match mp {
        MatchPosition::Leftmost => pos1 < pos2,
        MatchPosition::Rightmost => pos1 > pos2,
    }
}

pub fn find_first<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(usize::max_value());
    let consumer = FindConsumer::new(&find_op, MatchPosition::Leftmost, &best_found);
    pi.drive_unindexed(consumer)
}

pub fn find_last<PAR_ITER, FIND_OP>(pi: PAR_ITER, find_op: FIND_OP) -> Option<PAR_ITER::Item>
    where PAR_ITER: ParallelIterator,
          FIND_OP: Fn(&PAR_ITER::Item) -> bool + Sync
{
    let best_found = AtomicUsize::new(0);
    let consumer = FindConsumer::new(&find_op, MatchPosition::Rightmost, &best_found);
    pi.drive_unindexed(consumer)
}

struct FindConsumer<'f, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    lower_bound: Cell<usize>,
    upper_bound: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
}

impl<'f, FIND_OP> FindConsumer<'f, FIND_OP> {
    fn new(find_op: &'f FIND_OP,
           match_position: MatchPosition,
           best_found: &'f AtomicUsize) -> Self {
        FindConsumer {
            find_op: find_op,
            lower_bound: Cell::new(0),
            upper_bound: usize::max_value(),
            match_position: match_position,
            best_found: best_found,
        }
    }

    fn current_index(&self) -> usize {
        match self.match_position {
            MatchPosition::Leftmost => self.lower_bound.get(),
            MatchPosition::Rightmost => self.upper_bound
        }
    }
}

impl<'f, ITEM, FIND_OP> Consumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    type Folder = FindFolder<'f, ITEM, FIND_OP>;
    type Reducer = FindReducer;
    type Result = Option<ITEM>;

    fn cost(&mut self, cost: f64) -> f64 {
        cost * FUNC_ADJUSTMENT
    }

    fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) {
        let dir = self.match_position;
        (self.split_off(),
         self,
         FindReducer { match_position: dir })
    }

    fn into_folder(self) -> Self::Folder {
        FindFolder {
            find_op: self.find_op,
            boundary: self.current_index(),
            match_position: self.match_position,
            best_found: self.best_found,
            item: None,
        }
    }

    fn full(&self) -> bool {
        // can stop consuming if the best found index so far is *strictly*
        // better than anything this consumer will find
        better_position(self.best_found.load(Ordering::Relaxed),
                        self.current_index(),
                        self.match_position)
    }
}

#[test]
fn same_range_first_consumers_return_correct_answer() {
    let find_op = |x: &i32| x % 2 == 0;
    let first_found = AtomicUsize::new(usize::max_value());
    let far_right_consumer = FindConsumer::new(&find_op, MatchPosition::Leftmost, &first_found);

    // We save a consumer that will be far to the right of the main consumer (and therefore not
    // sharing an index range with that consumer) for fullness testing
    let consumer = far_right_consumer.split_off();

    // split until we have an indivisible range
    let bits_in_usize = usize::min_value().count_zeros();
    for i in 0..bits_in_usize {
        consumer.split_off();
    }

    let reducer = consumer.to_reducer();
    // the left and right folders should now have the same range, having
    // exhausted the resolution of usize
    let left_folder = consumer.split_off().into_folder();
    let right_folder = consumer.into_folder();

    let left_folder = left_folder.consume(0).consume(1);
    assert_eq!(left_folder.boundary, right_folder.boundary);
    // expect not full even though a better match has been found because the
    // ranges are the same
    assert!(!right_folder.full());
    assert!(far_right_consumer.full());
    let right_folder = right_folder.consume(2).consume(3);
    assert_eq!(reducer.reduce(left_folder.complete(), right_folder.complete()),
               Some(0));
}

#[test]
fn same_range_last_consumers_return_correct_answer() {
    let find_op = |x: &i32| x % 2 == 0;
    let last_found = AtomicUsize::new(0);
    let consumer = FindConsumer::new(&find_op, MatchPosition::Rightmost, &last_found);

    // We save a consumer that will be far to the left of the main consumer (and therefore not
    // sharing an index range with that consumer) for fullness testing
    let far_left_consumer = consumer.split_off();

    // split until we have an indivisible range
    let bits_in_usize = usize::min_value().count_zeros();
    for i in 0..bits_in_usize {
        consumer.split_off();
    }

    let reducer = consumer.to_reducer();
    // due to the exact calculation in split_off, the very last consumer has a
    // range of width 2, so we use the second-to-last consumer instead to get
    // the same boundary on both folders
    let consumer = consumer.split_off();
    let left_folder = consumer.split_off().into_folder();
    let right_folder = consumer.into_folder();
    let right_folder = right_folder.consume(2).consume(3);
    assert_eq!(left_folder.boundary, right_folder.boundary);
    // expect not full even though a better match has been found because the
    // ranges are the same
    assert!(!left_folder.full());
    assert!(far_left_consumer.full());
    let left_folder = left_folder.consume(0).consume(1);
    assert_eq!(reducer.reduce(left_folder.complete(), right_folder.complete()),
               Some(2));
}

impl<'f, ITEM, FIND_OP> UnindexedConsumer<ITEM> for FindConsumer<'f, FIND_OP>
    where ITEM: Send,
          FIND_OP: Fn(&ITEM) -> bool + Sync
{
    fn split_off(&self) -> Self {
        // Upper bound for one consumer will be lower bound for the other. This
        // overlap is okay, because only one of the bounds will be used for
        // comparing against best_found; the other is kept only to be able to
        // divide the range in half.
        //
        // When the resolution of usize has been exhausted (i.e. when
        // upper_bound = lower_bound), both results of this split will have the
        // same range. When that happens, we lose the ability to tell one
        // consumer to stop working when the other finds a better match, but the
        // reducer ensures that the best answer is still returned (see the test
        // above).
        //
        // This code assumes that the caller of split_off will use the result as
        // the *left* side of this iterator, and the remainder of self as the
        // *right* side.
        let old_lower_bound = self.lower_bound.get();
        let median = old_lower_bound + ((self.upper_bound - old_lower_bound) / 2);
        self.lower_bound.set(median);

        FindConsumer {
            find_op: self.find_op,
            lower_bound: Cell::new(old_lower_bound),
            upper_bound: median,
            match_position: self.match_position,
            best_found: self.best_found,
        }
    }

    fn to_reducer(&self) -> Self::Reducer {
        FindReducer { match_position: self.match_position }
    }
}

struct FindFolder<'f, ITEM, FIND_OP: 'f> {
    find_op: &'f FIND_OP,
    boundary: usize,
    match_position: MatchPosition,
    best_found: &'f AtomicUsize,
    item: Option<ITEM>,
}

impl<'f, FIND_OP: 'f + Fn(&ITEM) -> bool, ITEM> Folder<ITEM> for FindFolder<'f, ITEM, FIND_OP> {
    type Result = Option<ITEM>;

    fn consume(mut self, item: ITEM) -> Self {
        let found_best_in_range = match self.match_position {
            MatchPosition::Leftmost => self.item.is_some(),
            MatchPosition::Rightmost => false,
        };

        if !found_best_in_range && (self.find_op)(&item) {
            // Continuously try to set best_found until we succeed or we
            // discover a better match was already found.
            let mut current = self.best_found.load(Ordering::Relaxed);
            let mut exchange_result = Result::Err(0);
            while !better_position(current, self.boundary, self.match_position) &&
                  exchange_result.is_err() {
                exchange_result = self.best_found.compare_exchange_weak(current,
                                                                        self.boundary,
                                                                        Ordering::Relaxed,
                                                                        Ordering::Relaxed);
                if exchange_result.is_err() {
                    current = self.best_found.load(Ordering::Relaxed);
                }
            }

            if exchange_result.is_ok() {
                self.item = Some(item);
            }
        }
        self
    }

    fn complete(self) -> Self::Result {
        self.item
    }

    fn full(&self) -> bool {
        let found_best_in_range = match self.match_position {
            MatchPosition::Leftmost => self.item.is_some(),
            MatchPosition::Rightmost => false,
        };

        found_best_in_range ||
            better_position(self.best_found.load(Ordering::Relaxed),
                            self.boundary,
                            self.match_position)
    }
}

// These tests requires that a folder be assigned to an iterator with more than
// one element. We can't necessarily determine when that will happen for a given
// input to find_first/find_last, so we test the folder directly here instead.
#[test]
fn find_first_folder_does_not_clobber_first_found() {
    let best_found = AtomicUsize::new(usize::max_value());
    let f = FindFolder {
        find_op: &(|&x: &i32| -> bool { true }),
        boundary: 0,
        match_position: MatchPosition::Leftmost,
        best_found: &best_found,
        item: None,
    };
    let f = f.consume(0_i32).consume(1_i32).consume(2_i32);
    assert!(f.full());
    assert_eq!(f.complete(), Some(0_i32));
}

#[test]
fn find_last_folder_yields_last_match() {
    let best_found = AtomicUsize::new(0);
    let f = FindFolder {
        find_op: &(|&x: &i32| -> bool { true }),
        boundary: 0,
        match_position: MatchPosition::Rightmost,
        best_found: &best_found,
        item: None,
    };
    let f = f.consume(0_i32).consume(1_i32).consume(2_i32);
    assert_eq!(f.complete(), Some(2_i32));
}

struct FindReducer {
    match_position: MatchPosition
}

impl<ITEM> Reducer<Option<ITEM>> for FindReducer {
    fn reduce(self, left: Option<ITEM>, right: Option<ITEM>) -> Option<ITEM> {
        match self.match_position {
            MatchPosition::Leftmost => left.or(right),
            MatchPosition::Rightmost => right.or(left)
        }
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*!
* Internal class to handle OpenAl context and device.
*
* Work as a Singleton, check_al_context must be called before each OpenAl object
* to be sure that the context is created.
*/

#![macro_escape]
#![allow(raw_pointer_deriving)]

use std::cell::RefCell;
use std::ptr;
use openal::ffi;
use record_context;
use record_context::RecordContext;

// static al_context: local_data::Key<Box<OpenAlData>> = &local_data::Key;
local_data_key!(al_context: RefCell<Box<OpenAlData>>)

#[deriving(Clone)]
pub struct OpenAlData {
    al_context: *ffi::ALCcontext,
    al_device: *ffi::ALCdevice,
    al_capt_device:  *ffi::ALCdevice
}

impl OpenAlData {
    /**
     * Create a new OpenAlData struct
     *
     * Private method.
     */
    fn new() -> Result<OpenAlData, ~str> {
        let device = unsafe { ffi::alcOpenDevice(ptr::null()) };
        if device.is_null() {
            return Err("Internal error: cannot open the default device.".to_owned());
        }
        let context = unsafe { ffi::alcCreateContext(device, ptr::null()) };
        if context.is_null() {
            return Err("Internal error: cannot create the OpenAL context.".to_owned());
        }
        if unsafe { ffi::alcMakeContextCurrent(context) } == ffi::ALC_FALSE {
            return Err("Internal error: cannot make the OpenAL context current.".to_owned());
        }

        Ok(
            OpenAlData {
                al_context: context,
                al_device: device,
                al_capt_device: ptr::null()
            }
        )
    }

    /**
     * Check if the context is created.
     *
     * This function check is the OpenAl context is already created.
     * If context doesn't exist, create it, and store it in a local_data,
     * else get it from the local data and return it.
     *
     * # Return
     * A result containing nothing if the OpenAlData struct exist,
     * otherwise an error message.
     */
    pub fn check_al_context() -> Result<(), ~str> {
        if unsafe { ffi::alcGetCurrentContext() } != ptr::null() {
            return Ok(())
        }
        // local_data::get(al_context, |openal_data| {
        //     match openal_data {
        //         Some(_)     => Ok(()),
        //         None        => {
        //             match OpenAlData::new() {
        //                 Ok(al_data) => {
        //                     local_data::set(al_context, box al_data); Ok(())
        //                 },
        //                 Err(err)    => Err(err)
        //             }
        //         }
        //     }
        // })
        match al_context.get() {
            Some(_) => Ok(()),
            None    => {
                match OpenAlData::new() {
                    Ok(al_data) => {
                        al_context.replace(Some(RefCell::new(box al_data))); Ok(())
                    },
                    Err(err)    => Err(err)
                }
            }
        }
    }

    fn is_input_context_init() -> Result<RecordContext, ~str> {
        let is_some = al_context.get().is_some();
        if is_some {
            let mut new_context = *(*al_context.get().unwrap()).borrow().clone();
            if new_context.al_capt_device.is_not_null() {
                Ok(record_context::new(new_context.al_capt_device))
            } else {
                if "ALC_EXT_CAPTURE".with_c_str(|c_str| unsafe {
                    ffi::alcIsExtensionPresent(new_context.al_device, c_str) }) == ffi::ALC_FALSE {
                    return Err("Error: no input device available on your system.".to_owned())
                } else {
                    new_context.al_capt_device = unsafe {
                        ffi::alcCaptureOpenDevice(ptr::null(),
                                                  44100,
                                                  ffi::AL_FORMAT_MONO16,
                                                  44100) };
                    if new_context.al_capt_device.is_null() {
                        Err("Internal error: cannot open the default capture device.".to_owned())
                    } else {
                        let cap_device = new_context.al_capt_device;
                        al_context.replace(Some(RefCell::new(box new_context)));
                        Ok(record_context::new(cap_device))
                    }
                }
            }
        } else {
            Err("Error: you must request the input context, \
                           in the task where you initialize ears.".to_owned())
        }
    }
        // local_data::get_mut(al_context, |openal_data| {
        //     match openal_data {
        //         Some(d) => {
        //             if d.al_capt_device.is_not_null() {
        //                 Ok(record_context::new(d.al_capt_device))
        //             } else {
        //                 if "ALC_EXT_CAPTURE".with_c_str(|c_str| unsafe {
        //                     ffi::alcIsExtensionPresent(d.al_device, c_str) }) == ffi::ALC_FALSE {
        //                     return Err("Error: no input device available on your system.".to_owned())
        //                 } else {
        //                     d.al_capt_device = unsafe {
        //                         ffi::alcCaptureOpenDevice(ptr::null(),
        //                                                   44100,
        //                                                   ffi::AL_FORMAT_MONO16,
        //                                                   44100) };
        //                     if d.al_capt_device.is_null() {
        //                         Err("Internal error: cannot open the default capture device.".to_owned())
        //                     } else {
        //                         Ok(record_context::new(d.al_capt_device))
        //                     }
        //                 }
        //             }
        //         }   ,
        //         None    => Err("Error: you must request the input context, \
        //                        in the task where you initialize ears.".to_owned())
        //     }
        // })
    // }

    /**
     * Check if the input context is created.
     *
     * This function check if the input OpenAl context is already created.
     * The input openAL context need the normal AL context + its own extension.
     * So check if the context exist first, then load the input extension.
     *
     * # Return
     * A result containing nothing if the OpenAlData struct exist,
     * otherwise an error message.
     */
    pub fn check_al_input_context() -> Result<RecordContext, ~str> {
        if unsafe { ffi::alcGetCurrentContext() } != ptr::null() {
            OpenAlData::is_input_context_init()
        } else {
            match OpenAlData::check_al_context() {
                Ok(_)       => OpenAlData::is_input_context_init(),
                Err(err)    => Err(err)
            }
        }
    }
}

impl Drop for OpenAlData {
    fn drop(&mut self) {
        unsafe {
            ffi::alcDestroyContext(self.al_context);
            if self.al_capt_device.is_not_null() {
                ffi::alcCaptureCloseDevice(self.al_capt_device);
            }
            ffi::alcCloseDevice(self.al_device);
        }
    }
}

macro_rules! check_openal_context(
    ($def_ret:expr) => (
            match OpenAlData::check_al_context() {
                Ok(_)       => {},
                Err(err)    => { println!("{}", err); return $def_ret; }
            }
        );
)
<commit_msg>remove commented code<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*!
* Internal class to handle OpenAl context and device.
*
* Work as a Singleton, check_al_context must be called before each OpenAl object
* to be sure that the context is created.
*/

#![macro_escape]
#![allow(raw_pointer_deriving)]

use std::cell::RefCell;
use std::ptr;
use openal::ffi;
use record_context;
use record_context::RecordContext;

// static al_context: local_data::Key<Box<OpenAlData>> = &local_data::Key;
local_data_key!(al_context: RefCell<Box<OpenAlData>>)

#[deriving(Clone)]
pub struct OpenAlData {
    al_context: *ffi::ALCcontext,
    al_device: *ffi::ALCdevice,
    al_capt_device:  *ffi::ALCdevice
}

impl OpenAlData {
    /**
     * Create a new OpenAlData struct
     *
     * Private method.
     */
    fn new() -> Result<OpenAlData, ~str> {
        let device = unsafe { ffi::alcOpenDevice(ptr::null()) };
        if device.is_null() {
            return Err("Internal error: cannot open the default device.".to_owned());
        }
        let context = unsafe { ffi::alcCreateContext(device, ptr::null()) };
        if context.is_null() {
            return Err("Internal error: cannot create the OpenAL context.".to_owned());
        }
        if unsafe { ffi::alcMakeContextCurrent(context) } == ffi::ALC_FALSE {
            return Err("Internal error: cannot make the OpenAL context current.".to_owned());
        }

        Ok(
            OpenAlData {
                al_context: context,
                al_device: device,
                al_capt_device: ptr::null()
            }
        )
    }

    /**
     * Check if the context is created.
     *
     * This function check is the OpenAl context is already created.
     * If context doesn't exist, create it, and store it in a local_data,
     * else get it from the local data and return it.
     *
     * # Return
     * A result containing nothing if the OpenAlData struct exist,
     * otherwise an error message.
     */
    pub fn check_al_context() -> Result<(), ~str> {
        if unsafe { ffi::alcGetCurrentContext() } != ptr::null() {
            return Ok(())
        }
        match al_context.get() {
            Some(_) => Ok(()),
            None    => {
                match OpenAlData::new() {
                    Ok(al_data) => {
                        al_context.replace(Some(RefCell::new(box al_data))); Ok(())
                    },
                    Err(err)    => Err(err)
                }
            }
        }
    }

    fn is_input_context_init() -> Result<RecordContext, ~str> {
        let is_some = al_context.get().is_some();
        if is_some {
            let mut new_context = *(*al_context.get().unwrap()).borrow().clone();
            if new_context.al_capt_device.is_not_null() {
                Ok(record_context::new(new_context.al_capt_device))
            } else {
                if "ALC_EXT_CAPTURE".with_c_str(|c_str| unsafe {
                    ffi::alcIsExtensionPresent(new_context.al_device, c_str) }) == ffi::ALC_FALSE {
                    return Err("Error: no input device available on your system.".to_owned())
                } else {
                    new_context.al_capt_device = unsafe {
                        ffi::alcCaptureOpenDevice(ptr::null(),
                                                  44100,
                                                  ffi::AL_FORMAT_MONO16,
                                                  44100) };
                    if new_context.al_capt_device.is_null() {
                        Err("Internal error: cannot open the default capture device.".to_owned())
                    } else {
                        let cap_device = new_context.al_capt_device;
                        al_context.replace(Some(RefCell::new(box new_context)));
                        Ok(record_context::new(cap_device))
                    }
                }
            }
        } else {
            Err("Error: you must request the input context, \
                           in the task where you initialize ears.".to_owned())
        }
    }

    /**
     * Check if the input context is created.
     *
     * This function check if the input OpenAl context is already created.
     * The input openAL context need the normal AL context + its own extension.
     * So check if the context exist first, then load the input extension.
     *
     * # Return
     * A result containing nothing if the OpenAlData struct exist,
     * otherwise an error message.
     */
    pub fn check_al_input_context() -> Result<RecordContext, ~str> {
        if unsafe { ffi::alcGetCurrentContext() } != ptr::null() {
            OpenAlData::is_input_context_init()
        } else {
            match OpenAlData::check_al_context() {
                Ok(_)       => OpenAlData::is_input_context_init(),
                Err(err)    => Err(err)
            }
        }
    }
}

impl Drop for OpenAlData {
    fn drop(&mut self) {
        unsafe {
            ffi::alcDestroyContext(self.al_context);
            if self.al_capt_device.is_not_null() {
                ffi::alcCaptureCloseDevice(self.al_capt_device);
            }
            ffi::alcCloseDevice(self.al_device);
        }
    }
}

macro_rules! check_openal_context(
    ($def_ret:expr) => (
            match OpenAlData::check_al_context() {
                Ok(_)       => {},
                Err(err)    => { println!("{}", err); return $def_ret; }
            }
        );
)
<|endoftext|>