<commit_before>use address::{Address, Addressable};
use messages;
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket, IpAddr, Ipv4Addr};
use std::collections::HashMap;
use std::cmp;
use time;
use transaction::TransactionId;

pub const FAILED_TO_RESPOND_THRESHOLD: usize = 5;
pub const MINUTES_UNTIL_QUESTIONABLE: i64 = 15;

pub fn deserialize(message: &messages::protobufs::Node) -> Node {
    let ip = message.get_ip_address();
    let ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]);
    let port = message.get_port() as u16;
    let address = Address::from_str(message.get_id()).unwrap();
    Node::new(address, (ip, port))
}

pub trait Serialize {
    fn serialize(&self) -> messages::protobufs::Node;
}

#[derive(Debug, PartialEq)]
pub enum Status {
    Good,
    Questionable,
    Bad
}

#[derive(Debug)]
pub struct Node {
    address: Address,
    socket_address: SocketAddr,
    pending_queries: HashMap<TransactionId, time::Tm>,
    has_ever_responded: bool,
    last_received_query: time::Tm,
    last_received_response: time::Tm,
}

impl Node {
    pub fn new<S: ToSocketAddrs>(address: Address, socket_address: S) -> Node {
        let socket_address = socket_address
            .to_socket_addrs()
            .unwrap()
            .next()
            .unwrap();
        Node {
            address: address,
            socket_address: socket_address,
            pending_queries: HashMap::new(),
            has_ever_responded: false,
            last_received_query: time::now_utc(),
            last_received_response: time::now_utc()
        }
    }

    fn status(&self) -> Status {
        let time_since_last_seen = time::now_utc() - self.last_seen();

        if self.has_ever_responded &&
            time_since_last_seen < time::Duration::minutes(MINUTES_UNTIL_QUESTIONABLE) {
            Status::Good
        } else if self.pending_query_count() < FAILED_TO_RESPOND_THRESHOLD {
            Status::Questionable
        } else {
            Status::Bad
        }
    }

    pub fn is_bad(&self) -> bool {
        self.status() == Status::Bad
    }

    pub fn is_questionable(&self) -> bool {
        self.status() == Status::Questionable
    }

    pub fn last_seen(&self) -> time::Tm {
        cmp::max(self.last_received_query, self.last_received_response)
    }

    pub fn pending_query_count(&self) -> usize {
        self.pending_queries.len()
    }

    pub fn received_query(&mut self, _: TransactionId) {
        self.last_received_query = time::now_utc();
    }

    pub fn received_response(&mut self, transaction_id: TransactionId) {
        self.last_received_response = time::now_utc();
        if let Some(_queried_at) = self.pending_queries.remove(&transaction_id) {
            self.has_ever_responded = true;
            //let time_to_respond = time::now_utc() - queried_at;
            //debug!("{} took {:?} to respond", transaction_id, time_to_respond);
        } else {
            debug!("{:?} was not expecting response to {}", self.address, transaction_id);
            debug!("pending queries: {:?}", self.pending_queries.len());
        }
    }

    pub fn send(&self, message: Vec<u8>) {
        let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
        socket.send_to(&message[..], self.socket_address).unwrap();
    }

    pub fn sent_query(&mut self, transaction_id: TransactionId) {
        self.pending_queries.insert(transaction_id, time::now_utc());
    }

    // TODO: this is a temporary measure until a node can support multiple connections. Rather than
    // updating its only connection, it should just add another to the set and keep track of the
    // most reliable connection.
    pub fn update_connection(&mut self, other_node: Self) {
        self.socket_address = other_node.socket_address;
    }
}

impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        self.address() == other.address()
    }
}

impl Serialize for Node {
    fn serialize(&self) -> messages::protobufs::Node {
        let mut message = messages::protobufs::Node::new();
        message.set_id(self.address.to_str());
        match self.socket_address.ip() {
            IpAddr::V4(ipv4_addr) => {
                message.set_ip_address(ipv4_addr.octets().iter().cloned().collect());
            }
            IpAddr::V6(_) => {
                // TODO ipv6 node support
            }
        }
        message.set_port(self.socket_address.port() as u32);
        message
    }
}

impl Addressable for Node {
    fn address(&self) -> Address {
        self.address
    }
}

#[cfg(test)]
pub mod tests {
    use address::Address;
    use std::collections::HashMap;
    use std::net::ToSocketAddrs;
    use super::{FAILED_TO_RESPOND_THRESHOLD, MINUTES_UNTIL_QUESTIONABLE, Node, Serialize};
    use time;
    use transaction::TransactionId;

    pub fn new(address: Address, last_received_response: time::Tm, pending_queries: HashMap<TransactionId, time::Tm>) -> Node {
        use rand::{thread_rng, Rng};
        let port = thread_rng().gen_range(1000, 10000);
        Node {
            address: address,
            socket_address: ("0.0.0.0", port).to_socket_addrs().unwrap().next().unwrap(),
            pending_queries: pending_queries,
            has_ever_responded: false,
            last_received_query: time::empty_tm(),
            last_received_response: last_received_response
        }
    }

    pub fn good(address: Address) -> Node {
        new(address, time::now_utc(), HashMap::new())
    }

    pub fn questionable(address: Address) -> Node {
        let last_received_response = time::now_utc() -
            time::Duration::minutes(MINUTES_UNTIL_QUESTIONABLE);

        new(address, last_received_response, HashMap::new())
    }

    pub fn bad(address: Address) -> Node {
        let last_received_response = time::now_utc() -
            time::Duration::minutes(MINUTES_UNTIL_QUESTIONABLE + 1);
        let mut pending_queries = HashMap::new();
        let n_queries = FAILED_TO_RESPOND_THRESHOLD + 1;
        for i in 0..n_queries {
            pending_queries.insert(i as TransactionId, time::now_utc());
        }

        new(address, last_received_response, pending_queries)
    }

    #[test]
    fn test_received_response() {
        let address = Address::for_content("some string");
        let mut node = Node::new(address, ("0.0.0.0", 9000));

        // When it's not expecting the response
        node.received_response(1);
        assert!(!node.has_ever_responded);

        // When it's expecting the response
        let last_seen_before = node.last_seen();
        let transaction_id = 2;
        node.sent_query(transaction_id);
        node.received_response(transaction_id);
        let last_seen_after = node.last_seen();
        assert!(last_seen_before < last_seen_after);
        assert!(node.has_ever_responded);
    }

    #[test]
    fn test_serialize() {
        use messages;
        let mut protobuf = messages::protobufs::Node::new();
        protobuf.set_id("8b45e4bd1c6acb88bebf6407d16205f567e62a3e".to_string());
        protobuf.set_ip_address(vec![192, 168, 1, 2]);
        protobuf.set_port(9000);
        let address = Address::for_content("some string");
        let node = Node::new(address, ("192.168.1.2", 9000));
        assert_eq!(node.serialize(), protobuf);
    }
}
<commit_msg>Add documentation to node module<commit_after>use address::{Address, Addressable};
use messages;
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket, IpAddr, Ipv4Addr};
use std::collections::HashMap;
use std::cmp;
use time;
use transaction::TransactionId;

/// The maximum number of queries sent without a response before a node is considered bad.
pub const FAILED_TO_RESPOND_THRESHOLD: usize = 5;

/// A node becomes questionable if it hasn't been heard from in this many minutes.
pub const MINUTES_UNTIL_QUESTIONABLE: i64 = 15;

/// Deserialize a `Node` from a protobuf.
pub fn deserialize(message: &messages::protobufs::Node) -> Node {
    let ip = message.get_ip_address();
    let ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]);
    let port = message.get_port() as u16;
    let address = Address::from_str(message.get_id()).unwrap();
    Node::new(address, (ip, port))
}

/// Anything that needs to be serialized for transfer or storage.
///
/// TODO: this and `deserialize` should probably both be a part of a `Serializable` trait so that
/// other types can impl it instead of the `incoming` and `outgoing` functions in `messages`.
pub trait Serialize {
    fn serialize(&self) -> messages::protobufs::Node;
}

#[derive(Debug, PartialEq)]
pub enum Status {
    Good,
    Questionable,
    Bad
}

/// A `Node` is a peer in the network. It represents another network participant such as ourself.
/// It has an address, and a means to be sent messages. When we receive messages, they come from
/// other nodes.
///
/// Currently, a node is connected via UDP socket, but in a future iteration, a Node will be
/// decoupled from the underlying connection method. A Node semantically represents a
/// "participant," i.e. a person, robot, agent, etc. Not a single device, or single IP address.
///
/// That participant may be connected to the network via a variety of means. Most common would be
/// over the internet via UDP, but it could very well include more connections via LAN, Bluetooth,
/// shortwave radio, signal lamp, or any other medium that can transmit packets.
///
/// This in an important feature to the goals of comm. The comm network should be able to overlay
/// partitioned networks. If a subnetwork is connected via Bluetooth, and at least one participant
/// has a connection to the larger network, all participants are thereby connected to the larger
/// network.
#[derive(Debug)]
pub struct Node {
    address: Address,
    socket_address: SocketAddr,
    pending_queries: HashMap<TransactionId, time::Tm>,
    has_ever_responded: bool,
    last_received_query: time::Tm,
    last_received_response: time::Tm,
}

impl Node {
    pub fn new<S: ToSocketAddrs>(address: Address, socket_address: S) -> Node {
        let socket_address = socket_address
            .to_socket_addrs()
            .unwrap()
            .next()
            .unwrap();
        Node {
            address: address,
            socket_address: socket_address,
            pending_queries: HashMap::new(),
            has_ever_responded: false,
            last_received_query: time::now_utc(),
            last_received_response: time::now_utc()
        }
    }

    fn status(&self) -> Status {
        let time_since_last_seen = time::now_utc() - self.last_seen();

        if self.has_ever_responded &&
            time_since_last_seen < time::Duration::minutes(MINUTES_UNTIL_QUESTIONABLE) {
            Status::Good
        } else if self.pending_query_count() < FAILED_TO_RESPOND_THRESHOLD {
            Status::Questionable
        } else {
            Status::Bad
        }
    }

    /// Whether the node should be considered bad or unreliable. A bad node has not been heard from
    /// in the last `MINUTES_UNTIL_QUESTIONABLE` minutes, and hasn't responded to at least
    /// `FAILED_TO_RESPOND_THRESHOLD` queries.
    ///
    /// Generally, a bad node SHOULD NOT be sent queries, since it's likely to be a waste of
    /// network traffic. However, the node should not forgotten until its bucket is full, cannot be
    /// split, and a new node is being inserted into it. At that point, it's advantageous to remove
    /// the worst node from the bucket and insert another in its place.
    ///
    /// If a bad node begins responding to queries, it can become good again. The goal is to
    /// minimize wasted network traffic, but also to minimize network volatility. Repeatedly
    /// ejecting and reintroducing flaky nodes makes for a volatile network.
    ///
    /// TODO: Should we forgive a bad node's `pending_query_count()` if it becomes good again?
    pub fn is_bad(&self) -> bool {
        self.status() == Status::Bad
    }

    /// Whether the node is questionable and should therefore be pinged. A questionable node SHOULD
    /// still be sent relevant queries, and SHOULD be given every benefit of being treated as if it
    /// were good. Questionable only means that we don't know whether it's still good, and we
    /// should ask it.
    pub fn is_questionable(&self) -> bool {
        self.status() == Status::Questionable
    }

    /// The last time we received either a query or a response from a node.
    pub fn last_seen(&self) -> time::Tm {
        cmp::max(self.last_received_query, self.last_received_response)
    }

    /// How many unanswered queries we've sent to a node.
    pub fn pending_query_count(&self) -> usize {
        self.pending_queries.len()
    }

    /// Update the `last_received_query` timestamp for a node. Any time the network receives a
    /// query from a node, this method should be called, passing in the query's `TransactionId`.
    ///
    /// The TID is currently unused, but may be useful in the future.
    pub fn received_query(&mut self, _: TransactionId) {
        self.last_received_query = time::now_utc();
    }

    /// Update the `last_received_response` timestamp if we're indeed waiting for a response to
    /// a query we previously sent with a TID of `transaction_id`.
    ///
    /// If we're not expecting a response from this node for a query we sent previously, this
    /// method call is ignored and the timestamp is left as is.
    pub fn received_response(&mut self, transaction_id: TransactionId) {
        self.last_received_response = time::now_utc();
        if let Some(_queried_at) = self.pending_queries.remove(&transaction_id) {
            self.has_ever_responded = true;
            //let time_to_respond = time::now_utc() - queried_at;
            //debug!("{} took {:?} to respond", transaction_id, time_to_respond);
        } else {
            debug!("{:?} was not expecting response to {}", self.address, transaction_id);
            debug!("pending queries: {:?}", self.pending_queries.len());
        }
    }

    /// Send an encoded message to a node via its UDP socket.
    ///
    /// In the future, a node will support numerous connection types, and it should be send using
    /// all connections that are live (i.e. not bad)
    pub fn send(&self, message: Vec<u8>) {
        let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
        socket.send_to(&message[..], self.socket_address).unwrap();
    }

    /// Records that we're expecting a response from this node for the TID `transaction_id`.
    ///
    /// TODO: Perhaps this method should be rolled together with a method like `send_query` that
    /// records the TID and calls `send`.
    pub fn sent_query(&mut self, transaction_id: TransactionId) {
        self.pending_queries.insert(transaction_id, time::now_utc());
    }

    /// Update the socket address and port of a node. This is useful for when a node disconnects
    /// and reconnects to the internet, or changes IP addresses, etc.
    ///
    /// In the future, a node will have n connections instead of one socket address, and we'd just
    /// add another to the set, and keep track of which are good, eventually ejecting dead
    /// connections.
    ///
    /// TODO: this is a temporary measure until a node can support multiple connections. Rather
    /// than updating its only connection, it should just add another to the set and keep track of
    /// the most reliable connection.
    pub fn update_connection(&mut self, other_node: Self) {
        self.socket_address = other_node.socket_address;
    }
}

impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        self.address() == other.address()
    }
}

impl Serialize for Node {
    fn serialize(&self) -> messages::protobufs::Node {
        let mut message = messages::protobufs::Node::new();
        message.set_id(self.address.to_str());
        match self.socket_address.ip() {
            IpAddr::V4(ipv4_addr) => {
                message.set_ip_address(ipv4_addr.octets().iter().cloned().collect());
            }
            IpAddr::V6(_) => {
                // TODO ipv6 node support
            }
        }
        message.set_port(self.socket_address.port() as u32);
        message
    }
}

impl Addressable for Node {
    fn address(&self) -> Address {
        self.address
    }
}

#[cfg(test)]
pub mod tests {
    use address::Address;
    use std::collections::HashMap;
    use std::net::ToSocketAddrs;
    use super::{FAILED_TO_RESPOND_THRESHOLD, MINUTES_UNTIL_QUESTIONABLE, Node, Serialize};
    use time;
    use transaction::TransactionId;

    pub fn new(address: Address, last_received_response: time::Tm, pending_queries: HashMap<TransactionId, time::Tm>) -> Node {
        use rand::{thread_rng, Rng};
        let port = thread_rng().gen_range(1000, 10000);
        Node {
            address: address,
            socket_address: ("0.0.0.0", port).to_socket_addrs().unwrap().next().unwrap(),
            pending_queries: pending_queries,
            has_ever_responded: false,
            last_received_query: time::empty_tm(),
            last_received_response: last_received_response
        }
    }

    pub fn good(address: Address) -> Node {
        new(address, time::now_utc(), HashMap::new())
    }

    pub fn questionable(address: Address) -> Node {
        let last_received_response = time::now_utc() -
            time::Duration::minutes(MINUTES_UNTIL_QUESTIONABLE);

        new(address, last_received_response, HashMap::new())
    }

    pub fn bad(address: Address) -> Node {
        let last_received_response = time::now_utc() -
            time::Duration::minutes(MINUTES_UNTIL_QUESTIONABLE + 1);
        let mut pending_queries = HashMap::new();
        let n_queries = FAILED_TO_RESPOND_THRESHOLD + 1;
        for i in 0..n_queries {
            pending_queries.insert(i as TransactionId, time::now_utc());
        }

        new(address, last_received_response, pending_queries)
    }

    #[test]
    fn test_received_response() {
        let address = Address::for_content("some string");
        let mut node = Node::new(address, ("0.0.0.0", 9000));

        // When it's not expecting the response
        node.received_response(1);
        assert!(!node.has_ever_responded);

        // When it's expecting the response
        let last_seen_before = node.last_seen();
        let transaction_id = 2;
        node.sent_query(transaction_id);
        node.received_response(transaction_id);
        let last_seen_after = node.last_seen();
        assert!(last_seen_before < last_seen_after);
        assert!(node.has_ever_responded);
    }

    #[test]
    fn test_serialize() {
        use messages;
        let mut protobuf = messages::protobufs::Node::new();
        protobuf.set_id("8b45e4bd1c6acb88bebf6407d16205f567e62a3e".to_string());
        protobuf.set_ip_address(vec![192, 168, 1, 2]);
        protobuf.set_port(9000);
        let address = Address::for_content("some string");
        let node = Node::new(address, ("192.168.1.2", 9000));
        assert_eq!(node.serialize(), protobuf);
    }
}
<|endoftext|>


<commit_before>use winapi::shared::ws2def::{AF_INET, AF_INET6, AF_UNSPEC, SOCKADDR};
use winapi::shared::winerror::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS};
use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH;
use winapi::shared::minwindef::*;
use winapi::ctypes::*;
use libc::{size_t, c_void,  malloc, free};

use data::*;

use std::collections::BTreeMap;
use std::io::{Write, Error};
use std::slice::from_raw_parts;
use std::{io, ptr, mem};
use std::ffi::CStr;

// unions with non-`Copy` fields are unstable (see issue #32836)
// #[repr(C)]
// union AlimentOrLengthIfIndex {
//     aliment: c_ulonglong,
//     length_ifindex: LengthIfIndex,
// }

#[repr(C)]
struct LengthIfIndex{
    length: ULONG,
    ifindex: DWORD,
}

#[repr(C)]
struct LengthFlags{
    length: ULONG,
    flags: DWORD,
}

#[repr(C)]
struct SoketAddress{
    lp_sockaddr: *mut SOCKADDR,
    i_sockaddr_length: c_int
}

#[repr(C)]
struct IpAdapterPrefix{
    aol: LengthIfIndex,
    next: *mut IpAdapterPrefix,
    address: SoketAddress,
    prefix_length: ULONG,
}

#[repr(C)]
struct IpAdapterUnicastAddress {
    aol: LengthFlags,
    next: *mut IpAdapterUnicastAddress,
    address: SoketAddress,
    prefix_origin: c_int,
    suffix_origin: c_int,
    dad_state: c_int,
    valid_lifetime: ULONG,
    preferred_lifetime: ULONG,
    lease_lifetime: ULONG,
}

const MAX_ADAPTER_ADDRESS_LENGTH: usize = 8;

#[repr(C)]
struct IpAdapterAddresses{
    aol: LengthIfIndex,
    next: *mut IpAdapterAddresses,
    adapter_name:  *mut c_char,
    first_unicass_address: *mut IpAdapterUnicastAddress,
    first_anycass_address: *const c_void,
    first_multicass_address: *const c_void,
    first_dns_server_address: *const c_void,
    dns_suffix: *mut wchar_t,
    description: *mut wchar_t,
    friendly_name:  *mut wchar_t,
    physical_address: [u8; MAX_ADAPTER_ADDRESS_LENGTH],
    physical_address_length: DWORD,
    flags: DWORD,
    mtu: DWORD,
    if_type: DWORD,
    oper_status: c_int,
    ipv6_if_index: DWORD,
    zone_indices:  [DWORD;16],
    first_prefix: *mut IpAdapterPrefix,
}

// https://msdn.microsoft.com/en-us/library/aa365915(v=vs.85).aspx
// C:\Program Files (x86)\Windows Kits\8.1\Include\um\IPHlpApi.h
#[link(name = "Iphlpapi")]
extern "system" {
    fn GetAdaptersAddresses(
        family: ULONG,
        flags: ULONG,
        reserved: *const c_void,
        addresses: *mut IpAdapterAddresses,
        size: *mut ULONG,
    ) -> ULONG;
}

const WORKING_BUFFER_SIZEL: size_t = 15000;

pub fn interfaces() -> io::Result<BTreeMap<String, Network>> {
    let mut new_size:ULONG = WORKING_BUFFER_SIZEL as ULONG;
    let mut p_adapter: *mut IpAdapterAddresses;
    loop {
        unsafe {
            p_adapter = malloc( WORKING_BUFFER_SIZEL) as *mut IpAdapterAddresses;
            if p_adapter.is_null() {
                panic!("Failed: malloc!");
            }
            let res_code = GetAdaptersAddresses (
                0,
                AF_UNSPEC as ULONG, // ipv4 & ipv6
                ptr::null(),
                p_adapter,
                &mut new_size as *mut ULONG
            );
            match res_code {
                // 0
                ERROR_SUCCESS => break,
                // 111, retry
                ERROR_BUFFER_OVERFLOW => {
                    new_size*=2;
                    free(p_adapter as *mut c_void);
                    continue;
                }
                _=> {
                    return Err( Error::last_os_error());
                }
            }
        }
    }

    let mut map = BTreeMap::new();
    // key->adapter_name, name-> friendly_name, maybe should use the adapter_name all.
    unsafe {
        let mut cur_p_adapter = p_adapter;
        while !cur_p_adapter.is_null() {
            // name, mac, etc
            let adapter_name = c_char_array_to_string( (*cur_p_adapter).adapter_name);
            // println!("adapter_name : {}", adapter_name);

            // let dns_suffix = u16_array_to_string((*cur_p_adapter).dns_suffix);
            // println!("dns_suffix   : {}", dns_suffix);

            let friendly_name =  u16_array_to_string((*cur_p_adapter).friendly_name);
            // println!("friendly_name: {}", friendly_name);

            // let description = u16_array_to_string((*cur_p_adapter).description);
            // println!("description  : {}", description);

            // let mac = physical_address_to_string(&(*cur_p_adapter).physical_address, (*cur_p_adapter).physical_address_length);
            // println!("mac          : {}", mac);

            let mut addrs = Vec::new();
            // ip
            let mut cur_p_addr = (*cur_p_adapter).first_unicass_address;
            while !cur_p_addr.is_null() {
                let addr = parse_addr((*cur_p_addr).address.lp_sockaddr);
                addrs.push(addr);
               // println!("{:?}", addr);
               // next addr
               cur_p_addr =(*cur_p_addr).next;
            }
            let network = Network {
                name: friendly_name,
                addrs: addrs.into_iter().map(|addr| NetworkAddrs {addr: addr, netmask: IpAddr::Unsupported}).collect()
            };
            map.insert(adapter_name,network);

            // next adapter
            cur_p_adapter = (*cur_p_adapter).next;
        }
    }

    unsafe { free(p_adapter as *mut c_void); }
    Ok(map)
}

fn u16_array_to_string(p: *const u16) ->String {
    use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
    unsafe {
        if p.is_null() {
            return String::new();
        }
        let mut amt = 0usize;
        while !p.offset(amt as isize).is_null() &&  *p.offset(amt as isize) != 0u16 {
            amt+=1;
        }
        let u16s = from_raw_parts(p, amt);
        decode_utf16(u16s.iter().cloned())
        .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
        .collect::<String>()
    }
}

fn c_char_array_to_string(p: *const c_char) -> String {
    unsafe {
        CStr::from_ptr(p).to_string_lossy().into_owned()
    }
}

fn physical_address_to_string(array: &[u8;8], length: DWORD) -> String {
    let mut bytes = Vec::with_capacity(length as usize);
    for idx in 0..length as usize {
        if idx == 0  {
            write!(&mut bytes, "{:02X}", array[idx]).unwrap();
        } else {
            write!(&mut bytes, "-{:02X}", array[idx]).unwrap();
        }
    }
    String::from_utf8_lossy(&bytes[..]).into_owned()
}

// Thanks , copy from unix.rs and some modify
fn parse_addr(aptr: *const SOCKADDR) -> IpAddr {
    if aptr == ptr::null() {
        return IpAddr::Empty;
    }
    let addr = unsafe { *aptr };
    match addr.sa_family as i32 {
        AF_INET => IpAddr::V4(Ipv4Addr::new(addr.sa_data[2] as u8, addr.sa_data[3] as u8,
                                            addr.sa_data[4] as u8, addr.sa_data[5] as u8)),
        AF_INET6 => {
            // This is horrible.
            let addr6: *const SOCKADDR_IN6_LH = unsafe { mem::transmute(aptr) };
            let mut a: [u8; 16] = unsafe { *(*addr6).sin6_addr.u.Byte() };
            &mut a[..].reverse();
            let a: [u16; 8] = unsafe { mem::transmute(a) };
            IpAddr::V6(Ipv6Addr::new(a[7], a[6], a[5], a[4], a[3], a[2], a[1], a[0]))
        },
        _ => IpAddr::Unsupported,
    }
}<commit_msg>add on_link_prefix_length and netmask.<commit_after>use winapi::shared::ws2def::{AF_INET, AF_INET6, AF_UNSPEC, SOCKADDR};
use winapi::shared::winerror::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS};
use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH;
use winapi::shared::minwindef::*;
use winapi::ctypes::*;
use libc::{c_void, uint8_t, size_t, malloc, free};

use data::*;

use std::collections::BTreeMap;
use std::io::{Write, Error};
use std::slice::from_raw_parts;
use std::{io, ptr, mem};
use std::ffi::CStr;

// unions with non-`Copy` fields are unstable (see issue #32836)
// #[repr(C)]
// union AlimentOrLengthIfIndex {
//     aliment: c_ulonglong,
//     length_ifindex: LengthIfIndex,
// }

#[repr(C)]
struct LengthIfIndex{
    length: ULONG,
    ifindex: DWORD,
}

#[repr(C)]
struct LengthFlags{
    length: ULONG,
    flags: DWORD,
}

#[repr(C)]
struct SoketAddress{
    lp_sockaddr: *mut SOCKADDR,
    i_sockaddr_length: c_int
}

#[repr(C)]
struct IpAdapterPrefix{
    aol: LengthIfIndex,
    next: *mut IpAdapterPrefix,
    address: SoketAddress,
    prefix_length: ULONG,
}

#[repr(C)]
struct IpAdapterUnicastAddress {
    aol: LengthFlags,
    next: *mut IpAdapterUnicastAddress,
    address: SoketAddress,
    prefix_origin: c_int,
    suffix_origin: c_int,
    dad_state: c_int,
    valid_lifetime: ULONG,
    preferred_lifetime: ULONG,
    lease_lifetime: ULONG,
    on_link_prefix_length: uint8_t
}

const MAX_ADAPTER_ADDRESS_LENGTH: usize = 8;

#[repr(C)]
struct IpAdapterAddresses{
    aol: LengthIfIndex,
    next: *mut IpAdapterAddresses,
    adapter_name:  *mut c_char,
    first_unicass_address: *mut IpAdapterUnicastAddress,
    first_anycass_address: *const c_void,
    first_multicass_address: *const c_void,
    first_dns_server_address: *const c_void,
    dns_suffix: *mut wchar_t,
    description: *mut wchar_t,
    friendly_name:  *mut wchar_t,
    physical_address: [u8; MAX_ADAPTER_ADDRESS_LENGTH],
    physical_address_length: DWORD,
    flags: DWORD,
    mtu: DWORD,
    if_type: DWORD,
    oper_status: c_int,
    ipv6_if_index: DWORD,
    zone_indices:  [DWORD;16],
    first_prefix: *mut IpAdapterPrefix,
}

// https://msdn.microsoft.com/en-us/library/aa365915(v=vs.85).aspx
// https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa366066(d=printer,v=vs.85).aspx
// C:\Program Files (x86)\Windows Kits\8.1\Include\um\IPHlpApi.h
#[link(name = "Iphlpapi")]
extern "system" {
    fn GetAdaptersAddresses(
        family: ULONG,
        flags: ULONG,
        reserved: *const c_void,
        addresses: *mut IpAdapterAddresses,
        size: *mut ULONG,
    ) -> ULONG;
}

const WORKING_BUFFER_SIZEL: size_t = 15000;

pub fn interfaces() -> io::Result<BTreeMap<String, Network>> {
    let mut new_size:ULONG = WORKING_BUFFER_SIZEL as ULONG;
    let mut p_adapter: *mut IpAdapterAddresses;
    loop {
        unsafe {
            p_adapter = malloc( WORKING_BUFFER_SIZEL) as *mut IpAdapterAddresses;
            if p_adapter.is_null() {
                panic!("Failed: malloc!");
            }
            let res_code = GetAdaptersAddresses (
                0,
                AF_UNSPEC as ULONG, // ipv4 & ipv6
                ptr::null(),
                p_adapter,
                &mut new_size as *mut ULONG
            );
            match res_code {
                // 0
                ERROR_SUCCESS => break,
                // 111, retry
                ERROR_BUFFER_OVERFLOW => {
                    new_size*=2;
                    free(p_adapter as *mut c_void);
                    continue;
                }
                _=> {
                    return Err( Error::last_os_error());
                }
            }
        }
    }

    let mut map = BTreeMap::new();
    // key->adapter_name, name-> friendly_name, maybe should use the adapter_name all.
    unsafe {
        let mut cur_p_adapter = p_adapter;
        while !cur_p_adapter.is_null() {
            // name, mac, etc
            let adapter_name = c_char_array_to_string( (*cur_p_adapter).adapter_name);
            // println!("adapter_name : {}", adapter_name);

            // let dns_suffix = u16_array_to_string((*cur_p_adapter).dns_suffix);
            // println!("dns_suffix   : {}", dns_suffix);

            let friendly_name =  u16_array_to_string((*cur_p_adapter).friendly_name);
            // println!("friendly_name: {}", friendly_name);

            // let description = u16_array_to_string((*cur_p_adapter).description);
            // println!("description  : {}", description);

            // let mac = physical_address_to_string(&(*cur_p_adapter).physical_address, (*cur_p_adapter).physical_address_length);
            // println!("mac          : {}", mac);

            let mut addrs = Vec::new();
            // ip
            let mut cur_p_addr = (*cur_p_adapter).first_unicass_address;
            while !cur_p_addr.is_null() {
                let addr = parse_addr_and_netmask((*cur_p_addr).address.lp_sockaddr, (*cur_p_addr).on_link_prefix_length);
                addrs.push(addr);
               // println!("{:?}", addr);
               // next addr
               cur_p_addr =(*cur_p_addr).next;
            }
            let network = Network {
                name: friendly_name,
                addrs: addrs
            };
            map.insert(adapter_name,network);

            // next adapter
            cur_p_adapter = (*cur_p_adapter).next;
        }
    }

    unsafe { free(p_adapter as *mut c_void); }
    Ok(map)
}

fn u16_array_to_string(p: *const u16) ->String {
    use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
    unsafe {
        if p.is_null() {
            return String::new();
        }
        let mut amt = 0usize;
        while !p.offset(amt as isize).is_null() &&  *p.offset(amt as isize) != 0u16 {
            amt+=1;
        }
        let u16s = from_raw_parts(p, amt);
        decode_utf16(u16s.iter().cloned())
        .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
        .collect::<String>()
    }
}

fn c_char_array_to_string(p: *const c_char) -> String {
    unsafe {
        CStr::from_ptr(p).to_string_lossy().into_owned()
    }
}

fn physical_address_to_string(array: &[u8;8], length: DWORD) -> String {
    let mut bytes = Vec::with_capacity(length as usize);
    for idx in 0..length as usize {
        if idx == 0  {
            write!(&mut bytes, "{:02X}", array[idx]).unwrap();
        } else {
            write!(&mut bytes, "-{:02X}", array[idx]).unwrap();
        }
    }
    String::from_utf8_lossy(&bytes[..]).into_owned()
}

// Thanks , copy from unix.rs and some modify
fn parse_addr_and_netmask(aptr: *const SOCKADDR, mut net_bits: uint8_t) -> NetworkAddrs {
    if aptr == ptr::null() {
        return NetworkAddrs {addr: IpAddr::Empty, netmask: IpAddr::Empty};
    }
    let addr = unsafe { *aptr };
    match addr.sa_family as i32 {
        AF_INET => {
           let addr = IpAddr::V4(Ipv4Addr::new(addr.sa_data[2] as u8, addr.sa_data[3] as u8,
                                            addr.sa_data[4] as u8, addr.sa_data[5] as u8));
            let netmask = if net_bits <= 32 {
                        let v = netmask_v4(net_bits);
                        IpAddr::V4(Ipv4Addr::new(v[0], v[1], v[2], v[3]))
                    } else {
                        IpAddr::Empty
                    };
            NetworkAddrs { addr, netmask }
        },
        AF_INET6 => {
            // This is horrible.
            let addr6: *const SOCKADDR_IN6_LH = unsafe { mem::transmute(aptr) };
            let mut a: [u8; 16] = unsafe { *(*addr6).sin6_addr.u.Byte() };
            &mut a[..].reverse();
            let a: [u16; 8] = unsafe { mem::transmute(a) };
            let addr = IpAddr::V6(Ipv6Addr::new(a[7], a[6], a[5], a[4], a[3], a[2], a[1], a[0]));
            let netmask = if net_bits <= 128 {
                    let v = netmask_v6(net_bits);
                    IpAddr::V6( Ipv6Addr::new(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]))
                } else {
                    IpAddr::Empty
                };
            NetworkAddrs { addr, netmask }
        },
        _ => NetworkAddrs { addr: IpAddr::Empty, netmask: IpAddr::Empty }
    }
}

fn netmask_v4(bits: u8) -> [u8;4] {
    let mut tmp = [0u8;4];
    (0..4).for_each(|idx|
        {
            match (bits as usize> idx*8, bits as usize> idx*8 +8) {
                (true, true) => {
                    tmp[idx] = 255;
                },
                (true, false) => {
                    tmp[idx] = 255 << 8 - (bits as usize- idx*8)  as usize;
                },
                _=> {}
            }
        }
    );
    tmp
}

fn netmask_v6(bits: u8) -> [u16;8] {
    let mut tmp = [0u16;8];
    (0..8).for_each(|idx|
        {
            match (bits as usize> idx*16, bits as usize> idx*16 +16) {
                (true, true) => {
                    tmp[idx] = 0xffff;
                },
                (true, false) => {
                    tmp[idx] = 0xffff << 16 - (bits as usize- idx*16)  as usize;
                },
                _=> {}
            }
        }
    );
    tmp
}<|endoftext|>


<commit_before>use libc::*;
use ::list::{ENetList, ENetListNode};
use ::address::ENetAddress;
use ::{ENetChannel, ENetAcknowledgement, ENetIncomingCommand, ENetOutgoingCommand};
use ::host::ENetHost;
use ::protocol::ENetProtocol;
use packet::ENetPacket;

pub const ENET_PEER_DEFAULT_ROUND_TRIP_TIME: size_t = 500;
pub const ENET_PEER_DEFAULT_PACKET_THROTTLE: size_t = 32;
pub const ENET_PEER_PACKET_THROTTLE_SCALE: size_t = 32;
pub const ENET_PEER_PACKET_THROTTLE_COUNTER: size_t = 7;
pub const ENET_PEER_PACKET_THROTTLE_ACCELERATION: size_t = 2;
pub const ENET_PEER_PACKET_THROTTLE_DECELERATION: size_t = 2;
pub const ENET_PEER_PACKET_THROTTLE_INTERVAL: size_t = 5000;
pub const ENET_PEER_PACKET_LOSS_SCALE: size_t = 1<<16;
pub const ENET_PEER_PACKET_LOSS_INTERVAL: size_t = 10000;
pub const ENET_PEER_WINDOW_SIZE_SCALE: size_t = 64 * 1024;
pub const ENET_PEER_TIMEOUT_LIMIT: size_t = 32;
pub const ENET_PEER_TIMEOUT_MINIMUM: size_t = 5000;
pub const ENET_PEER_TIMEOUT_MAXIMUM: size_t = 30000;
pub const ENET_PEER_PING_INTERVAL: size_t = 500;
pub const ENET_PEER_UNSEQUENCED_WINDOWS: size_t = 64;
pub const ENET_PEER_UNSEQUENCED_WINDOW_SIZE: size_t = 1024;
pub const ENET_PEER_FREE_UNSEQUENCED_WINDOWS: size_t = 32;
pub const ENET_PEER_RELIABLE_WINDOWS: size_t = 16;
pub const ENET_PEER_RELIABLE_WINDOW_SIZE: size_t = 0x1000;
pub const ENET_PEER_FREE_RELIABLE_WINDOWS: size_t = 8;

#[repr(C)]
pub struct ENetPeer {
    pub acknowledgements: ENetList,
    pub address: ENetAddress,
    pub channelCount: size_t,
    pub channels: *mut ENetChannel,
    pub connectID: uint32_t,
    pub data: *mut c_void,
    pub dispatchCommands: ENetList,
    pub dispatchList: ENetListNode,
    pub earliestTimeout: uint32_t,
    pub eventData: uint32_t,
    pub highestRoundTripTimeVariance: uint32_t,
    pub host: *mut ENetHost,
    pub incomingBandwidth: uint32_t,
    pub incomingBandwidthThrottleEpoch: uint32_t,
    pub outgoingBandwidth: uint32_t,
    pub outgoingBandwidthThrottleEpoch: uint32_t,
    pub outgoingDataTotal: uint32_t,
    pub outgoingPeerID: uint16_t,
    pub outgoingReliableCommands: ENetList,
    pub outgoingReliableSequenceNumber: uint16_t,
    pub outgoingSessionID: uint8_t,
    pub outgoingUnreliableCommands: ENetList,
    pub outgoingUnsequencedGroup: uint16_t,
    pub packetLoss: uint32_t,
    pub packetLossEpoch: uint32_t,
    pub packetLossVariance: uint32_t,
    pub packetLost: uint32_t,
    pub packetSent: uint32_t,
    pub packetThrottle: uint32_t,
    pub packetThrottleAcceleration: uint32_t,
    pub packetThrottleCounter: uint32_t,
    pub packetThrottleDeceleration: uint32_t,
    pub packetThrottleEpoch: uint32_t,
    pub packetThrottleInterval: uint32_t,
    pub packetThrottleLimit: uint32_t,
    pub pingInterval: uint32_t,
    pub reliabledataInTransit: uint32_t,
    pub roundTripTimeVariance: uint32_t,
    pub sentReliableCommands: ENetList,
    pub sentUnreliableCommands: ENetList,
    pub state: ENetPeerState,
    pub timeoutLimit: uint32_t,
    pub timeoutMaximum: uint32_t,
    pub timeoutMinimum: uint32_t,
    pub totalWaitingData: size_t,
    pub unsequencedWindows: [uint32_t; ENET_PEER_UNSEQUENCED_WINDOW_SIZE/32],
    pub windowSize: uint32_t,
}

#[repr(C)]
pub enum ENetPeerState {
    ENET_PEER_STATE_DISCONNECTED = 0,
    ENET_PEER_STATE_CONNECTING = 1,
    ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2,
    ENET_PEER_STATE_CONNECTION_PENDING = 3,
    ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4,
    ENET_PEER_STATE_CONNECTED = 5,
    ENET_PEER_STATE_DISCONNECT_LATER = 6,
    ENET_PEER_STATE_DISCONNECTING = 7,
    ENET_PEER_STATE_ACHNOWLEDGING_DISCONNECT = 8,
    ENET_PEER_STATE_ZOMBIE = 9,
}

extern {
    pub fn enet_peer_disconnect(peer: *mut ENetPeer, data: uint32_t);
    pub fn enet_peer_disconnect_later(peer: *mut ENetPeer, data: uint32_t);
    pub fn enet_peer_disconnect_now(peer: *mut ENetPeer, data: uint32_t);
    pub fn enet_peer_dispatch_incoming_reliable_commands(peer: *mut ENetPeer, channel: *mut ENetChannel);
    pub fn enet_peer_dispatch_incoming_unreliable_commands(peer: *mut ENetPeer, channel: *mut ENetChannel);
    pub fn enet_peer_on_connect(peer: *mut ENetPeer);
    pub fn enet_peer_on_disconnect(peer: *mut ENetPeer);
    pub fn enet_peer_ping(peer: *mut ENetPeer);
    pub fn enet_peer_ping_interval(peer: *mut ENetPeer, pingInterval: uint32_t);
    pub fn enet_peer_queue_acknowledgement(peer: *mut ENetPeer, command: *const ENetProtocol,
            sentTime: uint16_t) -> *mut ENetAcknowledgement;
    pub fn enet_peer_queue_incoming_command(peer: *mut ENetPeer, command: *const ENetProtocol,
            data: *const c_void, dataLength: size_t, flags: uint32_t, fragmentCount: uint32_t)
            -> *mut ENetIncomingCommand;
    pub fn enet_peer_queue_outgoing_command(peer: *mut ENetPeer, command: *const ENetProtocol,
            packet: *mut ENetPacket, offset: uint32_t, length: uint16_t) -> *mut ENetOutgoingCommand;
    pub fn enet_peer_receive(peer: *mut ENetPeer, channelID: *mut uint8_t) -> *mut ENetPacket;
    pub fn enet_peer_reset(peer: *mut ENetPeer);
    pub fn enet_peer_reset_queues(peer: *mut ENetPeer);
    pub fn enet_peer_send(peer: *mut ENetPeer, channelID: uint8_t, packet: *mut ENetPacket) -> c_int;
    pub fn enet_peer_setup_outgoing_command(peer: *mut ENetPeer,
            outgoingCommand: *mut ENetOutgoingCommand);
    pub fn enet_peer_throttle(peer: *mut ENetPeer, rtt: uint32_t) -> c_int;
    pub fn enet_peer_throttle_configure(peer: *mut ENetPeer, interval: uint32_t, acceleration: uint32_t,
            deceleration: uint32_t);
    pub fn enet_peer_timeout(peer: *mut ENetPeer, timeoutLimit: uint32_t, timeoutMinimum: uint32_t,
            timeoutMaximum: uint32_t);
}
<commit_msg>derive clone, copy, partialeq for ENetPeer, fix typo<commit_after>use libc::*;
use ::list::{ENetList, ENetListNode};
use ::address::ENetAddress;
use ::{ENetChannel, ENetAcknowledgement, ENetIncomingCommand, ENetOutgoingCommand};
use ::host::ENetHost;
use ::protocol::ENetProtocol;
use packet::ENetPacket;

pub const ENET_PEER_DEFAULT_ROUND_TRIP_TIME: size_t = 500;
pub const ENET_PEER_DEFAULT_PACKET_THROTTLE: size_t = 32;
pub const ENET_PEER_PACKET_THROTTLE_SCALE: size_t = 32;
pub const ENET_PEER_PACKET_THROTTLE_COUNTER: size_t = 7;
pub const ENET_PEER_PACKET_THROTTLE_ACCELERATION: size_t = 2;
pub const ENET_PEER_PACKET_THROTTLE_DECELERATION: size_t = 2;
pub const ENET_PEER_PACKET_THROTTLE_INTERVAL: size_t = 5000;
pub const ENET_PEER_PACKET_LOSS_SCALE: size_t = 1<<16;
pub const ENET_PEER_PACKET_LOSS_INTERVAL: size_t = 10000;
pub const ENET_PEER_WINDOW_SIZE_SCALE: size_t = 64 * 1024;
pub const ENET_PEER_TIMEOUT_LIMIT: size_t = 32;
pub const ENET_PEER_TIMEOUT_MINIMUM: size_t = 5000;
pub const ENET_PEER_TIMEOUT_MAXIMUM: size_t = 30000;
pub const ENET_PEER_PING_INTERVAL: size_t = 500;
pub const ENET_PEER_UNSEQUENCED_WINDOWS: size_t = 64;
pub const ENET_PEER_UNSEQUENCED_WINDOW_SIZE: size_t = 1024;
pub const ENET_PEER_FREE_UNSEQUENCED_WINDOWS: size_t = 32;
pub const ENET_PEER_RELIABLE_WINDOWS: size_t = 16;
pub const ENET_PEER_RELIABLE_WINDOW_SIZE: size_t = 0x1000;
pub const ENET_PEER_FREE_RELIABLE_WINDOWS: size_t = 8;

#[repr(C)]
pub struct ENetPeer {
    pub acknowledgements: ENetList,
    pub address: ENetAddress,
    pub channelCount: size_t,
    pub channels: *mut ENetChannel,
    pub connectID: uint32_t,
    pub data: *mut c_void,
    pub dispatchCommands: ENetList,
    pub dispatchList: ENetListNode,
    pub earliestTimeout: uint32_t,
    pub eventData: uint32_t,
    pub highestRoundTripTimeVariance: uint32_t,
    pub host: *mut ENetHost,
    pub incomingBandwidth: uint32_t,
    pub incomingBandwidthThrottleEpoch: uint32_t,
    pub outgoingBandwidth: uint32_t,
    pub outgoingBandwidthThrottleEpoch: uint32_t,
    pub outgoingDataTotal: uint32_t,
    pub outgoingPeerID: uint16_t,
    pub outgoingReliableCommands: ENetList,
    pub outgoingReliableSequenceNumber: uint16_t,
    pub outgoingSessionID: uint8_t,
    pub outgoingUnreliableCommands: ENetList,
    pub outgoingUnsequencedGroup: uint16_t,
    pub packetLoss: uint32_t,
    pub packetLossEpoch: uint32_t,
    pub packetLossVariance: uint32_t,
    pub packetLost: uint32_t,
    pub packetSent: uint32_t,
    pub packetThrottle: uint32_t,
    pub packetThrottleAcceleration: uint32_t,
    pub packetThrottleCounter: uint32_t,
    pub packetThrottleDeceleration: uint32_t,
    pub packetThrottleEpoch: uint32_t,
    pub packetThrottleInterval: uint32_t,
    pub packetThrottleLimit: uint32_t,
    pub pingInterval: uint32_t,
    pub reliabledataInTransit: uint32_t,
    pub roundTripTimeVariance: uint32_t,
    pub sentReliableCommands: ENetList,
    pub sentUnreliableCommands: ENetList,
    pub state: ENetPeerState,
    pub timeoutLimit: uint32_t,
    pub timeoutMaximum: uint32_t,
    pub timeoutMinimum: uint32_t,
    pub totalWaitingData: size_t,
    pub unsequencedWindows: [uint32_t; ENET_PEER_UNSEQUENCED_WINDOW_SIZE/32],
    pub windowSize: uint32_t,
}

#[repr(C)]
#[derive(Clone,Copy,PartialEq)]
pub enum ENetPeerState {
    ENET_PEER_STATE_DISCONNECTED = 0,
    ENET_PEER_STATE_CONNECTING = 1,
    ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2,
    ENET_PEER_STATE_CONNECTION_PENDING = 3,
    ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4,
    ENET_PEER_STATE_CONNECTED = 5,
    ENET_PEER_STATE_DISCONNECT_LATER = 6,
    ENET_PEER_STATE_DISCONNECTING = 7,
    ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8,
    ENET_PEER_STATE_ZOMBIE = 9,
}

extern {
    pub fn enet_peer_disconnect(peer: *mut ENetPeer, data: uint32_t);
    pub fn enet_peer_disconnect_later(peer: *mut ENetPeer, data: uint32_t);
    pub fn enet_peer_disconnect_now(peer: *mut ENetPeer, data: uint32_t);
    pub fn enet_peer_dispatch_incoming_reliable_commands(peer: *mut ENetPeer, channel: *mut ENetChannel);
    pub fn enet_peer_dispatch_incoming_unreliable_commands(peer: *mut ENetPeer, channel: *mut ENetChannel);
    pub fn enet_peer_on_connect(peer: *mut ENetPeer);
    pub fn enet_peer_on_disconnect(peer: *mut ENetPeer);
    pub fn enet_peer_ping(peer: *mut ENetPeer);
    pub fn enet_peer_ping_interval(peer: *mut ENetPeer, pingInterval: uint32_t);
    pub fn enet_peer_queue_acknowledgement(peer: *mut ENetPeer, command: *const ENetProtocol,
            sentTime: uint16_t) -> *mut ENetAcknowledgement;
    pub fn enet_peer_queue_incoming_command(peer: *mut ENetPeer, command: *const ENetProtocol,
            data: *const c_void, dataLength: size_t, flags: uint32_t, fragmentCount: uint32_t)
            -> *mut ENetIncomingCommand;
    pub fn enet_peer_queue_outgoing_command(peer: *mut ENetPeer, command: *const ENetProtocol,
            packet: *mut ENetPacket, offset: uint32_t, length: uint16_t) -> *mut ENetOutgoingCommand;
    pub fn enet_peer_receive(peer: *mut ENetPeer, channelID: *mut uint8_t) -> *mut ENetPacket;
    pub fn enet_peer_reset(peer: *mut ENetPeer);
    pub fn enet_peer_reset_queues(peer: *mut ENetPeer);
    pub fn enet_peer_send(peer: *mut ENetPeer, channelID: uint8_t, packet: *mut ENetPacket) -> c_int;
    pub fn enet_peer_setup_outgoing_command(peer: *mut ENetPeer,
            outgoingCommand: *mut ENetOutgoingCommand);
    pub fn enet_peer_throttle(peer: *mut ENetPeer, rtt: uint32_t) -> c_int;
    pub fn enet_peer_throttle_configure(peer: *mut ENetPeer, interval: uint32_t, acceleration: uint32_t,
            deceleration: uint32_t);
    pub fn enet_peer_timeout(peer: *mut ENetPeer, timeoutLimit: uint32_t, timeoutMinimum: uint32_t,
            timeoutMaximum: uint32_t);
}
<|endoftext|>


<commit_before>use pombase_rc_string::RcString;

use crate::web::config::Config;
use crate::web::data::{GeneDetails, OntAnnotationId};

fn make_title(config: &Config, gene_details: &GeneDetails) -> String {
    let name_and_uniquename =
        if let Some(ref name) = gene_details.name {
            format!("{} ({})", name, gene_details.uniquename)
        } else {
            format!("{}", gene_details.uniquename)
        };

    let feature_type =
        if gene_details.feature_type.ends_with(" gene") {
            String::from(gene_details.feature_type.as_str())
        } else {
            format!("{} gene", gene_details.feature_type)
        };

    if let Some(ref product) = gene_details.product {
        format!("{} - {} {} - {}", config.database_name, feature_type,
                name_and_uniquename, product)
    } else {
        format!("{} - {} {}", config.database_name, feature_type, name_and_uniquename)
    }
}

fn gene_header(config: &Config, title: &str) -> String  {
    format!(r##"
  <meta charset="utf-8">
  <title>{}</title>
  <base href="/">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="title" content="{}">
  <meta name="author" content="{}">
  <meta name="description" content="{}">
  <meta name="google-site-verification" content="H8vhCdG7XsSbh-8f20VSipvu2PnZ22YSGZm81jE0-Pk" />
  <link rel="shortcut icon" href="/assets/favicon.ico">
  <meta name="apple-mobile-web-app-title" content="PomBase">
  <meta name="application-name" content="PomBase">
"##,
            title, title, config.database_name, title
    )
}

fn gene_summary(config: &Config, gene_details: &GeneDetails) -> String {
    let mut summ = String::new();

    summ += "
\n";
    if let Some(ref name) = gene_details.name {
        summ += &format!("
Gene standard name

{}
\n",  name);
    }
    summ += &format!("
Systematic ID

{}
\n",  gene_details.uniquename);

    if let Some(gene_organism) = config.organism_by_taxonid(gene_details.taxonid) {
        summ += &format!("
Organism

{}\n", gene_organism.scientific_name());

        if gene_organism.alternative_names.len() > 0 {
            summ += &format!(" ({})", gene_organism.alternative_names.join(", "));
        }
        summ += "
\n";
    }

    summ += "
\n";

    summ
}

fn get_annotations(ont_annotation_ids: &Vec<OntAnnotationId>,
                   gene_details: &GeneDetails) -> String {
    let mut ret = String::new();
    let mut references = vec![];
    for ont_annotation_id in ont_annotation_ids {
        if let Some(annotation_details) = gene_details.annotation_details.get(ont_annotation_id) {
            if let Some(ref reference) = annotation_details.reference {
                references.push(reference);
            }
        }
    }

    if references.len() > 0 {
        references.sort();
        references.dedup();
        ret += "
References:
\n
";
        for reference in references {
            ret += &format!("
{}
\n", reference, reference);
        }
        ret += "
\n";
    }

    ret
}

fn gene_annotation(config: &Config, gene_details: &GeneDetails) -> String {
    let mut annotation_html = String::new();

    let mut cv_names: Vec<RcString> = vec![];

    for cv_name in gene_details.cv_annotations.keys() {
        cv_names.push(cv_name.clone());
    }

    let cmp_display_names =
        |cv_name_a: &RcString, cv_name_b: &RcString| {
            let cv_config_a = config.cv_config_by_name(&cv_name_a);
            let cv_display_name_a = cv_config_a.display_name;
            let cv_config_b = config.cv_config_by_name(&cv_name_b);
            let cv_display_name_b = cv_config_b.display_name;

            cv_display_name_a.cmp(&cv_display_name_b)
        };

    cv_names.sort_by(cmp_display_names);

    for cv_name in cv_names {
        let term_annotations = gene_details.cv_annotations.get(&cv_name).unwrap();
        let cv_config = config.cv_config_by_name(&cv_name);
        let cv_display_name = cv_config.display_name;
        annotation_html += &format!("<sect>\n
{}
\n", cv_display_name);
        for term_annotation in term_annotations {
            let term_name =
                if let Some(term_short_opt) =
                    gene_details.terms_by_termid.get(&term_annotation.term) {
                        if let Some(term_short) = term_short_opt {
                            String::from(term_short.name.as_str())
                        } else {
                            String::from("(unknown term name)")
                        }
                    } else {
                        String::from("(unknown term name)")
                    };
            annotation_html += &format!("<sect>
{} - \
{}
\n{}</sect>\n",
                                        term_annotation.term,
                                        term_annotation.term,
                                        term_annotation.term,
                                        term_name,
                                        get_annotations(&term_annotation.annotations,
                                                        gene_details));
        }
        annotation_html += "</sect>\n";
    }

    annotation_html
}

fn gene_body(config: &Config, title: &str, gene_details: &GeneDetails) -> String {
    let mut body = String::new();

    body += &format!("
{}
\n", title);

    body += &format!("<sect>
Welcome to PomBase
\n
{}
</sect>\n",
                     config.site_description);

    body += &format!("<sect>
Gene summary
\n{}</sect>\n",
                     gene_summary(config, gene_details));

    body += &format!("<sect>
Annotation
\n{}</sect>\n",
                     gene_annotation(config, gene_details));
    body
}

pub fn render_simple_gene_page(config: &Config, gene_details: &GeneDetails) -> String  {
    let title = make_title(config, gene_details);

    format! ("
<!DOCTYPE html>

    {}

    {}

",
          gene_header(config, &title),
          gene_body(config, &title, gene_details))
}
<commit_msg>Add synonyms, UniProt ID etc. to simple gene pages<commit_after>use pombase_rc_string::RcString;

use crate::web::config::Config;
use crate::web::data::{GeneDetails, OntAnnotationId};

fn make_title(config: &Config, gene_details: &GeneDetails) -> String {
    let name_and_uniquename =
        if let Some(ref name) = gene_details.name {
            format!("{} ({})", name, gene_details.uniquename)
        } else {
            format!("{}", gene_details.uniquename)
        };

    let feature_type =
        if gene_details.feature_type.ends_with(" gene") {
            String::from(gene_details.feature_type.as_str())
        } else {
            format!("{} gene", gene_details.feature_type)
        };

    if let Some(ref product) = gene_details.product {
        format!("{} - {} {} - {}", config.database_name, feature_type,
                name_and_uniquename, product)
    } else {
        format!("{} - {} {}", config.database_name, feature_type, name_and_uniquename)
    }
}

fn gene_header(config: &Config, title: &str) -> String  {
    format!(r##"
  <meta charset="utf-8">
  <title>{}</title>
  <base href="/">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="title" content="{}">
  <meta name="author" content="{}">
  <meta name="description" content="{}">
  <meta name="google-site-verification" content="H8vhCdG7XsSbh-8f20VSipvu2PnZ22YSGZm81jE0-Pk" />
  <link rel="shortcut icon" href="/assets/favicon.ico">
  <meta name="apple-mobile-web-app-title" content="PomBase">
  <meta name="application-name" content="PomBase">
"##,
            title, title, config.database_name, title
    )
}

fn gene_summary(config: &Config, gene_details: &GeneDetails) -> String {
    let mut summ = String::new();

    summ += "
\n";
    if let Some(ref name) = gene_details.name {
        summ += &format!("
Gene standard name

{}
\n",  name);
    }
    summ += &format!("
Systematic ID

{}
\n", gene_details.uniquename);

    if gene_details.synonyms.len() > 0 {
        let synonyms: Vec<RcString> =
            gene_details.synonyms.iter().map(|s| s.name.clone()).collect();
        summ += &format!("
Synonyms

{}
\n", synonyms.join(", "));
    }

    if let Some(ref uniprot_identifier) = gene_details.uniprot_identifier {
        summ += &format!("
UniProt ID

{}
\n", uniprot_identifier);
    }

    if let Some(ref orfeome_identifier) = gene_details.orfeome_identifier {
        summ += &format!("
ORFeome ID

{}
\n", orfeome_identifier);
    }

    if let Some(gene_organism) = config.organism_by_taxonid(gene_details.taxonid) {
        summ += &format!("
Organism

{}\n", gene_organism.scientific_name());

        if gene_organism.alternative_names.len() > 0 {
            summ += &format!(" ({})", gene_organism.alternative_names.join(", "));
        }
        summ += "
\n";
    }

    if let Some(ref characterisation_status) = gene_details.characterisation_status {
        summ += &format!("
Characterisation status

{}\n",
                         characterisation_status);
    }

    summ += "
\n";

    summ
}

fn get_annotations(ont_annotation_ids: &Vec<OntAnnotationId>,
                   gene_details: &GeneDetails) -> String {
    let mut ret = String::new();
    let mut references = vec![];
    for ont_annotation_id in ont_annotation_ids {
        if let Some(annotation_details) = gene_details.annotation_details.get(ont_annotation_id) {
            if let Some(ref reference) = annotation_details.reference {
                references.push(reference);
            }
        }
    }

    if references.len() > 0 {
        references.sort();
        references.dedup();
        ret += "
References:
\n
";
        for reference in references {
            ret += &format!("
{}
\n", reference, reference);
        }
        ret += "
\n";
    }

    ret
}

fn gene_annotation(config: &Config, gene_details: &GeneDetails) -> String {
    let mut annotation_html = String::new();

    let mut cv_names: Vec<RcString> = vec![];

    for cv_name in gene_details.cv_annotations.keys() {
        cv_names.push(cv_name.clone());
    }

    let cmp_display_names =
        |cv_name_a: &RcString, cv_name_b: &RcString| {
            let cv_config_a = config.cv_config_by_name(&cv_name_a);
            let cv_display_name_a = cv_config_a.display_name;
            let cv_config_b = config.cv_config_by_name(&cv_name_b);
            let cv_display_name_b = cv_config_b.display_name;

            cv_display_name_a.cmp(&cv_display_name_b)
        };

    cv_names.sort_by(cmp_display_names);

    for cv_name in cv_names {
        let term_annotations = gene_details.cv_annotations.get(&cv_name).unwrap();
        let cv_config = config.cv_config_by_name(&cv_name);
        let cv_display_name = cv_config.display_name;
        annotation_html += &format!("<sect>\n
{}
\n", cv_display_name);
        for term_annotation in term_annotations {
            let term_name =
                if let Some(term_short_opt) =
                    gene_details.terms_by_termid.get(&term_annotation.term) {
                        if let Some(term_short) = term_short_opt {
                            String::from(term_short.name.as_str())
                        } else {
                            String::from("(unknown term name)")
                        }
                    } else {
                        String::from("(unknown term name)")
                    };
            annotation_html += &format!("<sect>
{} - \
{}
\n{}</sect>\n",
                                        term_annotation.term,
                                        term_annotation.term,
                                        term_annotation.term,
                                        term_name,
                                        get_annotations(&term_annotation.annotations,
                                                        gene_details));
        }
        annotation_html += "</sect>\n";
    }

    annotation_html
}

fn gene_body(config: &Config, title: &str, gene_details: &GeneDetails) -> String {
    let mut body = String::new();

    body += &format!("
{}
\n", title);

    body += &format!("<sect>
Welcome to PomBase
\n
{}
</sect>\n",
                     config.site_description);

    body += &format!("<sect>
Gene summary
\n{}</sect>\n",
                     gene_summary(config, gene_details));

    body += &format!("<sect>
Annotation
\n{}</sect>\n",
                     gene_annotation(config, gene_details));
    body
}

pub fn render_simple_gene_page(config: &Config, gene_details: &GeneDetails) -> String  {
    let title = make_title(config, gene_details);

    format! ("
<!DOCTYPE html>

    {}

    {}

",
          gene_header(config, &title),
          gene_body(config, &title, gene_details))
}
<|endoftext|>


<commit_before>use nom::{
  be_u8, be_u16,
  IResult,
  ErrorCode, Err,
};

use frame::{
  ChannelAssignment, NumberType,
  Footer,
};

fn blocking_strategy(input: &[u8]) -> IResult<&[u8], bool> {
  match take!(input, 2) {
    IResult::Done(i, bytes)   => {
      let sync_code = ((bytes[0] as u16) << 6) +
                      (bytes[1] as u16) >> 2;
      let is_valid  = ((bytes[1] >> 1) & 0b01) == 0;

      if sync_code == 0b11111111111110 && is_valid {
        let is_variable_block_size = (bytes[1] & 0b01) == 1;

        IResult::Done(i, is_variable_block_size)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn block_sample(input: &[u8]) -> IResult<&[u8], (u32, u32)> {
  match take!(input, 1) {
    IResult::Done(i, bytes)   => {
      let sample_rate = bytes[0] & 0x0f;

      if sample_rate != 0x0f {
        let block_size = bytes[0] >> 4;

        IResult::Done(i, (block_size as u32, sample_rate as u32))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn channel_bits(input: &[u8]) -> IResult<&[u8], (ChannelAssignment, usize)> {
  match take!(input, 1) {
    IResult::Done(i, bytes)   => {
      let channel_assignment = match bytes[0] >> 4 {
        0b0000...0b0111 => ChannelAssignment::Independent,
        0b1000          => ChannelAssignment::LeftSide,
        0b1001          => ChannelAssignment::RightSide,
        0b1010          => ChannelAssignment::MiddleSide,
        _               => ChannelAssignment::Independent,
      };
      let bits_per_sample = (bytes[0] >> 1) & 0b0111;
      let is_valid        = (bytes[0] & 0b01) == 0;

      if is_valid {
        IResult::Done(i, (channel_assignment, bits_per_sample as usize))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn utf8_size(input: &[u8], is_u64: bool)
             -> IResult<&[u8], Option<(usize, u8)>> {
  map!(input, be_u8, |utf8_header| {
    match utf8_header {
      0b00000000...0b01111111 => Some((0, utf8_header)),
      0b11000000...0b11011111 => Some((1, utf8_header & 0b00011111)),
      0b11100000...0b11101111 => Some((2, utf8_header & 0b00001111)),
      0b11110000...0b11110111 => Some((3, utf8_header & 0b00000111)),
      0b11111000...0b11111011 => Some((4, utf8_header & 0b00000011)),
      0b11111100...0b11111101 => Some((5, utf8_header & 0b00000001)),
      0b11111110              => if is_u64 { Some((6, 0)) } else { None },
      _                       => None,
    }
  })
}

fn sample_or_frame_number(input: &[u8], is_sample: bool,
                          (size, value): (usize, u8))
                          -> IResult<&[u8], NumberType> {
  let mut result   = value as u64;
  let mut is_error = false;

  match take!(input, size) {
    IResult::Done(i, bytes)   => {
      for i in (0..size) {
        let byte = bytes[i] as u64;

        match byte {
          0b10000000...10111111 => {
            result = (result << 6) + (byte & 0b00111111);
          }
          _                     => {
            is_error = true;
            break;
          }
        }
      }

      if is_error {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      } else if is_sample {
        IResult::Done(i, NumberType::Sample(result))
      } else {
        IResult::Done(i, NumberType::Frame(result as u32))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

named!(footer <&[u8], Footer>, map!(be_u16, Footer));
<commit_msg>Add parser for secondary bit flags<commit_after>use nom::{
  be_u8, be_u16,
  IResult,
  ErrorCode, Err,
};

use frame::{
  ChannelAssignment, NumberType,
  Footer,
};

use utility::to_u32;

fn blocking_strategy(input: &[u8]) -> IResult<&[u8], bool> {
  match take!(input, 2) {
    IResult::Done(i, bytes)   => {
      let sync_code = ((bytes[0] as u16) << 6) +
                      (bytes[1] as u16) >> 2;
      let is_valid  = ((bytes[1] >> 1) & 0b01) == 0;

      if sync_code == 0b11111111111110 && is_valid {
        let is_variable_block_size = (bytes[1] & 0b01) == 1;

        IResult::Done(i, is_variable_block_size)
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn block_sample(input: &[u8]) -> IResult<&[u8], (u32, u32)> {
  match take!(input, 1) {
    IResult::Done(i, bytes)   => {
      let sample_rate = bytes[0] & 0x0f;

      if sample_rate != 0x0f {
        let block_size = bytes[0] >> 4;

        IResult::Done(i, (block_size as u32, sample_rate as u32))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn channel_bits(input: &[u8]) -> IResult<&[u8], (ChannelAssignment, usize)> {
  match take!(input, 1) {
    IResult::Done(i, bytes)   => {
      let channel_assignment = match bytes[0] >> 4 {
        0b0000...0b0111 => ChannelAssignment::Independent,
        0b1000          => ChannelAssignment::LeftSide,
        0b1001          => ChannelAssignment::RightSide,
        0b1010          => ChannelAssignment::MiddleSide,
        _               => ChannelAssignment::Independent,
      };
      let bits_per_sample = (bytes[0] >> 1) & 0b0111;
      let is_valid        = (bytes[0] & 0b01) == 0;

      if is_valid {
        IResult::Done(i, (channel_assignment, bits_per_sample as usize))
      } else {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn utf8_size(input: &[u8], is_u64: bool)
             -> IResult<&[u8], Option<(usize, u8)>> {
  map!(input, be_u8, |utf8_header| {
    match utf8_header {
      0b00000000...0b01111111 => Some((0, utf8_header)),
      0b11000000...0b11011111 => Some((1, utf8_header & 0b00011111)),
      0b11100000...0b11101111 => Some((2, utf8_header & 0b00001111)),
      0b11110000...0b11110111 => Some((3, utf8_header & 0b00000111)),
      0b11111000...0b11111011 => Some((4, utf8_header & 0b00000011)),
      0b11111100...0b11111101 => Some((5, utf8_header & 0b00000001)),
      0b11111110              => if is_u64 { Some((6, 0)) } else { None },
      _                       => None,
    }
  })
}

fn sample_or_frame_number(input: &[u8], is_sample: bool,
                          (size, value): (usize, u8))
                          -> IResult<&[u8], NumberType> {
  let mut result   = value as u64;
  let mut is_error = false;

  match take!(input, size) {
    IResult::Done(i, bytes)   => {
      for i in (0..size) {
        let byte = bytes[i] as u64;

        match byte {
          0b10000000...10111111 => {
            result = (result << 6) + (byte & 0b00111111);
          }
          _                     => {
            is_error = true;
            break;
          }
        }
      }

      if is_error {
        IResult::Error(Err::Position(ErrorCode::Digit as u32, input))
      } else if is_sample {
        IResult::Done(i, NumberType::Sample(result))
      } else {
        IResult::Done(i, NumberType::Frame(result as u32))
      }
    }
    IResult::Error(error)     => IResult::Error(error),
    IResult::Incomplete(need) => IResult::Incomplete(need),
  }
}

fn secondary_block_size(input: &[u8], block_byte: u32)
                        -> IResult<&[u8], Option<u32>> {
  match block_byte {
    0b0110 => opt!(input, map!(take!(1), to_u32)),
    0b0111 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

fn secondary_sample_rate(input: &[u8], sample_byte: u32)
                        -> IResult<&[u8], Option<u32>> {
  match sample_byte {
    0b1100 => opt!(input, map!(take!(1), to_u32)),
    0b1101 => opt!(input, map!(take!(2), to_u32)),
    0b1110 => opt!(input, map!(take!(2), to_u32)),
    _      => IResult::Done(input, None)
  }
}

named!(footer <&[u8], Footer>, map!(be_u16, Footer));
<|endoftext|>


<commit_before>use run::spawn_process;
use io::{ReaderUtil, WriterUtil};
use libc::{c_int, pid_t};

export run;

#[cfg(target_os = "win32")]
fn target_env(lib_path: ~str, prog: ~str) -> ~[(~str,~str)] {

    let mut env = os::env();

    // Make sure we include the aux directory in the path
    assert prog.ends_with(~".exe");
    let aux_path = prog.slice(0u, prog.len() - 4u) + ~".libaux";

    env = do vec::map(env) |pair| {
        let (k,v) = pair;
        if k == ~"PATH" { (~"PATH", v + ~";" + lib_path + ~";" + aux_path) }
        else { (k,v) }
    };
    if str::ends_with(prog, ~"rustc.exe") {
        env.push((~"RUST_THREADS", ~"1"));
    }
    return env;
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
fn target_env(_lib_path: ~str, _prog: ~str) -> ~[(~str,~str)] {
    ~[]
}

// FIXME (#2659): This code is duplicated in core::run::program_output
fn run(lib_path: ~str,
       prog: ~str,
       args: ~[~str],
       env: ~[(~str, ~str)],
       input: Option<~str>) -> {status: int, out: ~str, err: ~str} {

    let pipe_in = os::pipe();
    let pipe_out = os::pipe();
    let pipe_err = os::pipe();
    let pid = spawn_process(prog, args,
                            &Some(env + target_env(lib_path, prog)),
                            &None, pipe_in.in, pipe_out.out, pipe_err.out);

    os::close(pipe_in.in);
    os::close(pipe_out.out);
    os::close(pipe_err.out);
    if pid == -1i32 {
        os::close(pipe_in.out);
        os::close(pipe_out.in);
        os::close(pipe_err.in);
        fail;
    }

    writeclose(pipe_in.out, input);
    let p = pipes::PortSet();
    let ch = p.chan();
    do task::spawn_sched(task::SingleThreaded) {
        let errput = readclose(pipe_err.in);
        ch.send((2, errput));
    }
    let ch = p.chan();
    do task::spawn_sched(task::SingleThreaded) {
        let output = readclose(pipe_out.in);
        ch.send((1, output));
    }
    let status = run::waitpid(pid);
    let mut errs = ~"";
    let mut outs = ~"";
    let mut count = 2;
    while count > 0 {
        match p.recv() {
          (1, s) => {
            outs = s;
          }
          (2, s) => {
            errs = s;
          }
          _ => { fail }
        };
        count -= 1;
    };
    return {status: status, out: outs, err: errs};
}

fn writeclose(fd: c_int, s: Option<~str>) {
    if s.is_some() {
        let writer = io::fd_writer(fd, false);
        writer.write_str(s.get());
    }

    os::close(fd);
}

fn readclose(fd: c_int) -> ~str {
    // Copied from run::program_output
    let file = os::fdopen(fd);
    let reader = io::FILE_reader(file, false);
    let mut buf = ~"";
    while !reader.eof() {
        let bytes = reader.read_bytes(4096u);
        str::push_str(&mut buf, str::from_bytes(bytes));
    }
    os::fclose(file);
    return buf;
}
<commit_msg>Fix a compilation error on windows<commit_after>use run::spawn_process;
use io::{ReaderUtil, WriterUtil};
use libc::{c_int, pid_t};

export run;

#[cfg(target_os = "win32")]
fn target_env(lib_path: ~str, prog: ~str) -> ~[(~str,~str)] {

    let mut env = os::env();

    // Make sure we include the aux directory in the path
    assert prog.ends_with(~".exe");
    let aux_path = prog.slice(0u, prog.len() - 4u) + ~".libaux";

    env = do vec::map(env) |pair| {
        let (k,v) = *pair;
        if k == ~"PATH" { (~"PATH", v + ~";" + lib_path + ~";" + aux_path) }
        else { (k,v) }
    };
    if str::ends_with(prog, ~"rustc.exe") {
        env.push((~"RUST_THREADS", ~"1"));
    }
    return env;
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
fn target_env(_lib_path: ~str, _prog: ~str) -> ~[(~str,~str)] {
    ~[]
}

// FIXME (#2659): This code is duplicated in core::run::program_output
fn run(lib_path: ~str,
       prog: ~str,
       args: ~[~str],
       env: ~[(~str, ~str)],
       input: Option<~str>) -> {status: int, out: ~str, err: ~str} {

    let pipe_in = os::pipe();
    let pipe_out = os::pipe();
    let pipe_err = os::pipe();
    let pid = spawn_process(prog, args,
                            &Some(env + target_env(lib_path, prog)),
                            &None, pipe_in.in, pipe_out.out, pipe_err.out);

    os::close(pipe_in.in);
    os::close(pipe_out.out);
    os::close(pipe_err.out);
    if pid == -1i32 {
        os::close(pipe_in.out);
        os::close(pipe_out.in);
        os::close(pipe_err.in);
        fail;
    }

    writeclose(pipe_in.out, input);
    let p = pipes::PortSet();
    let ch = p.chan();
    do task::spawn_sched(task::SingleThreaded) {
        let errput = readclose(pipe_err.in);
        ch.send((2, errput));
    }
    let ch = p.chan();
    do task::spawn_sched(task::SingleThreaded) {
        let output = readclose(pipe_out.in);
        ch.send((1, output));
    }
    let status = run::waitpid(pid);
    let mut errs = ~"";
    let mut outs = ~"";
    let mut count = 2;
    while count > 0 {
        match p.recv() {
          (1, s) => {
            outs = s;
          }
          (2, s) => {
            errs = s;
          }
          _ => { fail }
        };
        count -= 1;
    };
    return {status: status, out: outs, err: errs};
}

fn writeclose(fd: c_int, s: Option<~str>) {
    if s.is_some() {
        let writer = io::fd_writer(fd, false);
        writer.write_str(s.get());
    }

    os::close(fd);
}

fn readclose(fd: c_int) -> ~str {
    // Copied from run::program_output
    let file = os::fdopen(fd);
    let reader = io::FILE_reader(file, false);
    let mut buf = ~"";
    while !reader.eof() {
        let bytes = reader.read_bytes(4096u);
        str::push_str(&mut buf, str::from_bytes(bytes));
    }
    os::fclose(file);
    return buf;
}
<|endoftext|>


<commit_before>use anyhow::*;
use diesel;
use diesel::prelude::*;
#[cfg(feature = "profile-index")]
use flame;
use log::{error, info};
use rayon::prelude::*;
use regex::Regex;
use std::fs;
use std::path::Path;
use std::time;

use crate::config::MiscSettings;
use crate::db::{directories, misc_settings, songs, DB};
use crate::metadata;
use crate::vfs::VFSSource;

const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 1000; // Insertions in each transaction
const INDEX_BUILDING_CLEAN_BUFFER_SIZE: usize = 500; // Insertions in each transaction

pub fn update(db: &DB) -> Result<()> {
	let start = time::Instant::now();
	info!("Beginning library index update");
	clean(db)?;
	populate(db)?;
	info!(
		"Library index update took {} seconds",
		start.elapsed().as_secs()
	);
	#[cfg(feature = "profile-index")]
	flame::dump_html(&mut fs::File::create("index-flame-graph.html").unwrap()).unwrap();
	Ok(())
}

#[derive(Debug, Insertable)]
#[table_name = "songs"]
struct NewSong {
	path: String,
	parent: String,
	track_number: Option<i32>,
	disc_number: Option<i32>,
	title: Option<String>,
	artist: Option<String>,
	album_artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	duration: Option<i32>,
}

#[derive(Debug, Insertable)]
#[table_name = "directories"]
struct NewDirectory {
	path: String,
	parent: Option<String>,
	artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	date_added: i32,
}

struct IndexBuilder {
	new_songs: Vec<NewSong>,
	new_directories: Vec<NewDirectory>,
	db: DB,
	album_art_pattern: Regex,
}

impl IndexBuilder {
	#[cfg_attr(feature = "profile-index", flame)]
	fn new(db: DB, album_art_pattern: Regex) -> Result<IndexBuilder> {
		let mut new_songs = Vec::new();
		let mut new_directories = Vec::new();
		new_songs.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
		new_directories.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
		Ok(IndexBuilder {
			new_songs,
			new_directories,
			db,
			album_art_pattern,
		})
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn flush_songs(&mut self) -> Result<()> {
		let connection = self.db.connect()?;
		diesel::insert_into(songs::table)
			.values(&self.new_songs)
			.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
		self.new_songs.clear();
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn flush_directories(&mut self) -> Result<()> {
		let connection = self.db.connect()?;
		diesel::insert_into(directories::table)
			.values(&self.new_directories)
			.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
		self.new_directories.clear();
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_song(&mut self, song: NewSong) -> Result<()> {
		if self.new_songs.len() >= self.new_songs.capacity() {
			self.flush_songs()?;
		}
		self.new_songs.push(song);
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_directory(&mut self, directory: NewDirectory) -> Result<()> {
		if self.new_directories.len() >= self.new_directories.capacity() {
			self.flush_directories()?;
		}
		self.new_directories.push(directory);
		Ok(())
	}

	fn get_artwork(&self, dir: &Path) -> Result<Option<String>> {
		for file in fs::read_dir(dir)? {
			let file = file?;
			if let Some(name_string) = file.file_name().to_str() {
				if self.album_art_pattern.is_match(name_string) {
					return Ok(file.path().to_str().map(|p| p.to_owned()));
				}
			}
		}
		Ok(None)
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn populate_directory(&mut self, parent: Option<&Path>, path: &Path) -> Result<()> {
		// Find artwork
		let artwork = self.get_artwork(path).unwrap_or(None);

		// Extract path and parent path
		let parent_string = parent.and_then(|p| p.to_str()).map(|s| s.to_owned());
		let path_string = path.to_str().ok_or(anyhow!("Invalid directory path"))?;

		// Find date added
		let metadata = fs::metadata(path_string)?;
		let created = metadata
			.created()
			.or_else(|_| metadata.modified())?
			.duration_since(time::UNIX_EPOCH)?
			.as_secs() as i32;

		let mut directory_album = None;
		let mut directory_year = None;
		let mut directory_artist = None;
		let mut inconsistent_directory_album = false;
		let mut inconsistent_directory_year = false;
		let mut inconsistent_directory_artist = false;

		// Sub directories
		let mut sub_directories = Vec::new();

		// Insert content
		for file in fs::read_dir(path)? {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("directory-entry");
			let file_path = match file {
				Ok(ref f) => f.path(),
				_ => {
					error!("File read error within {}", path_string);
					break;
				}
			};

			if file_path.is_dir() {
				sub_directories.push(file_path.to_path_buf());
				continue;
			}

			if let Some(file_path_string) = file_path.to_str() {
				if let Ok(tags) = metadata::read(file_path.as_path()) {
					if tags.year.is_some() {
						inconsistent_directory_year |=
							directory_year.is_some() && directory_year != tags.year;
						directory_year = tags.year;
					}

					if tags.album.is_some() {
						inconsistent_directory_album |=
							directory_album.is_some() && directory_album != tags.album;
						directory_album = tags.album.as_ref().cloned();
					}

					if tags.album_artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.album_artist;
						directory_artist = tags.album_artist.as_ref().cloned();
					} else if tags.artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.artist;
						directory_artist = tags.artist.as_ref().cloned();
					}

					let song = NewSong {
						path: file_path_string.to_owned(),
						parent: path_string.to_owned(),
						disc_number: tags.disc_number.map(|n| n as i32),
						track_number: tags.track_number.map(|n| n as i32),
						title: tags.title,
						duration: tags.duration.map(|n| n as i32),
						artist: tags.artist,
						album_artist: tags.album_artist,
						album: tags.album,
						year: tags.year,
						artwork: artwork.as_ref().cloned(),
					};

					self.push_song(song)?;
				}
			}
		}

		// Insert directory
		if inconsistent_directory_year {
			directory_year = None;
		}
		if inconsistent_directory_album {
			directory_album = None;
		}
		if inconsistent_directory_artist {
			directory_artist = None;
		}

		let directory = NewDirectory {
			path: path_string.to_owned(),
			parent: parent_string,
			artwork,
			album: directory_album,
			artist: directory_artist,
			year: directory_year,
			date_added: created,
		};
		self.push_directory(directory)?;

		// Populate subdirectories
		for sub_directory in sub_directories {
			self.populate_directory(Some(path), &sub_directory)?;
		}

		Ok(())
	}
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn clean(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;

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

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

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

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

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

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

	Ok(())
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn populate(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;
	let mount_points = vfs.get_mount_points();

	let album_art_pattern;
	{
		let connection = db.connect()?;
		let settings: MiscSettings = misc_settings::table.get_result(&connection)?;
		album_art_pattern = Regex::new(&settings.index_album_art_pattern)?;
	}

	let mut builder = IndexBuilder::new(db.clone(), album_art_pattern)?;
	for target in mount_points.values() {
		builder.populate_directory(None, target.as_path())?;
	}
	builder.flush_songs()?;
	builder.flush_directories()?;
	Ok(())
}
<commit_msg>More accurate index duration display<commit_after>use anyhow::*;
use diesel;
use diesel::prelude::*;
#[cfg(feature = "profile-index")]
use flame;
use log::{error, info};
use rayon::prelude::*;
use regex::Regex;
use std::fs;
use std::path::Path;
use std::time;

use crate::config::MiscSettings;
use crate::db::{directories, misc_settings, songs, DB};
use crate::metadata;
use crate::vfs::VFSSource;

const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 1000; // Insertions in each transaction
const INDEX_BUILDING_CLEAN_BUFFER_SIZE: usize = 500; // Insertions in each transaction

pub fn update(db: &DB) -> Result<()> {
	let start = time::Instant::now();
	info!("Beginning library index update");
	clean(db)?;
	populate(db)?;
	info!(
		"Library index update took {} seconds",
		start.elapsed().as_millis() as f32 / 1000.0
	);
	#[cfg(feature = "profile-index")]
	flame::dump_html(&mut fs::File::create("index-flame-graph.html").unwrap()).unwrap();
	Ok(())
}

#[derive(Debug, Insertable)]
#[table_name = "songs"]
struct NewSong {
	path: String,
	parent: String,
	track_number: Option<i32>,
	disc_number: Option<i32>,
	title: Option<String>,
	artist: Option<String>,
	album_artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	duration: Option<i32>,
}

#[derive(Debug, Insertable)]
#[table_name = "directories"]
struct NewDirectory {
	path: String,
	parent: Option<String>,
	artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	date_added: i32,
}

struct IndexBuilder {
	new_songs: Vec<NewSong>,
	new_directories: Vec<NewDirectory>,
	db: DB,
	album_art_pattern: Regex,
}

impl IndexBuilder {
	#[cfg_attr(feature = "profile-index", flame)]
	fn new(db: DB, album_art_pattern: Regex) -> Result<IndexBuilder> {
		let mut new_songs = Vec::new();
		let mut new_directories = Vec::new();
		new_songs.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
		new_directories.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);
		Ok(IndexBuilder {
			new_songs,
			new_directories,
			db,
			album_art_pattern,
		})
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn flush_songs(&mut self) -> Result<()> {
		let connection = self.db.connect()?;
		diesel::insert_into(songs::table)
			.values(&self.new_songs)
			.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
		self.new_songs.clear();
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn flush_directories(&mut self) -> Result<()> {
		let connection = self.db.connect()?;
		diesel::insert_into(directories::table)
			.values(&self.new_directories)
			.execute(&*connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
		self.new_directories.clear();
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_song(&mut self, song: NewSong) -> Result<()> {
		if self.new_songs.len() >= self.new_songs.capacity() {
			self.flush_songs()?;
		}
		self.new_songs.push(song);
		Ok(())
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_directory(&mut self, directory: NewDirectory) -> Result<()> {
		if self.new_directories.len() >= self.new_directories.capacity() {
			self.flush_directories()?;
		}
		self.new_directories.push(directory);
		Ok(())
	}

	fn get_artwork(&self, dir: &Path) -> Result<Option<String>> {
		for file in fs::read_dir(dir)? {
			let file = file?;
			if let Some(name_string) = file.file_name().to_str() {
				if self.album_art_pattern.is_match(name_string) {
					return Ok(file.path().to_str().map(|p| p.to_owned()));
				}
			}
		}
		Ok(None)
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn populate_directory(&mut self, parent: Option<&Path>, path: &Path) -> Result<()> {
		// Find artwork
		let artwork = self.get_artwork(path).unwrap_or(None);

		// Extract path and parent path
		let parent_string = parent.and_then(|p| p.to_str()).map(|s| s.to_owned());
		let path_string = path.to_str().ok_or(anyhow!("Invalid directory path"))?;

		// Find date added
		let metadata = fs::metadata(path_string)?;
		let created = metadata
			.created()
			.or_else(|_| metadata.modified())?
			.duration_since(time::UNIX_EPOCH)?
			.as_secs() as i32;

		let mut directory_album = None;
		let mut directory_year = None;
		let mut directory_artist = None;
		let mut inconsistent_directory_album = false;
		let mut inconsistent_directory_year = false;
		let mut inconsistent_directory_artist = false;

		// Sub directories
		let mut sub_directories = Vec::new();

		// Insert content
		for file in fs::read_dir(path)? {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("directory-entry");
			let file_path = match file {
				Ok(ref f) => f.path(),
				_ => {
					error!("File read error within {}", path_string);
					break;
				}
			};

			if file_path.is_dir() {
				sub_directories.push(file_path.to_path_buf());
				continue;
			}

			if let Some(file_path_string) = file_path.to_str() {
				if let Ok(tags) = metadata::read(file_path.as_path()) {
					if tags.year.is_some() {
						inconsistent_directory_year |=
							directory_year.is_some() && directory_year != tags.year;
						directory_year = tags.year;
					}

					if tags.album.is_some() {
						inconsistent_directory_album |=
							directory_album.is_some() && directory_album != tags.album;
						directory_album = tags.album.as_ref().cloned();
					}

					if tags.album_artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.album_artist;
						directory_artist = tags.album_artist.as_ref().cloned();
					} else if tags.artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.artist;
						directory_artist = tags.artist.as_ref().cloned();
					}

					let song = NewSong {
						path: file_path_string.to_owned(),
						parent: path_string.to_owned(),
						disc_number: tags.disc_number.map(|n| n as i32),
						track_number: tags.track_number.map(|n| n as i32),
						title: tags.title,
						duration: tags.duration.map(|n| n as i32),
						artist: tags.artist,
						album_artist: tags.album_artist,
						album: tags.album,
						year: tags.year,
						artwork: artwork.as_ref().cloned(),
					};

					self.push_song(song)?;
				}
			}
		}

		// Insert directory
		if inconsistent_directory_year {
			directory_year = None;
		}
		if inconsistent_directory_album {
			directory_album = None;
		}
		if inconsistent_directory_artist {
			directory_artist = None;
		}

		let directory = NewDirectory {
			path: path_string.to_owned(),
			parent: parent_string,
			artwork,
			album: directory_album,
			artist: directory_artist,
			year: directory_year,
			date_added: created,
		};
		self.push_directory(directory)?;

		// Populate subdirectories
		for sub_directory in sub_directories {
			self.populate_directory(Some(path), &sub_directory)?;
		}

		Ok(())
	}
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn clean(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;

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

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

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

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

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

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

	Ok(())
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn populate(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;
	let mount_points = vfs.get_mount_points();

	let album_art_pattern;
	{
		let connection = db.connect()?;
		let settings: MiscSettings = misc_settings::table.get_result(&connection)?;
		album_art_pattern = Regex::new(&settings.index_album_art_pattern)?;
	}

	let mut builder = IndexBuilder::new(db.clone(), album_art_pattern)?;
	for target in mount_points.values() {
		builder.populate_directory(None, target.as_path())?;
	}
	builder.flush_songs()?;
	builder.flush_directories()?;
	Ok(())
}
<|endoftext|>