<commit_before>#[link(name = "http",
       vers = "0.1-pre",
       uuid = "d2ad8df0-547a-4ce1-99c6-a9da3b98fb3e",
       url = "")];

#[comment = "Rust HTTP server"];
#[license = "MIT/ASL2"];
#[crate_type = "lib"];

#[deny(non_camel_case_types)];
//#[deny(missing_doc)];

#[feature(macro_rules)];
#[macro_escape];

extern mod extra;

pub mod buffer;
pub mod client;
pub mod common;
pub mod server;
pub mod method;
pub mod headers;
pub mod rfc2616;
#[path = "generated/status.rs"]
pub mod status;  // Getting an error? It's generated; use ``make`` or see the ``Makefile``

/// TODO: submit upstream
pub mod memstream;
<commit_msg>Update http link metadata.<commit_after>#[link(name = "http",
       package_id = "http",
       vers = "0.1-pre",
       uuid = "d2ad8df0-547a-4ce1-99c6-a9da3b98fb3e",
       url = "https://github.com/chris-morgan/rust-http/tree/master/src/http")];

#[comment = "Rust HTTP server"];
#[license = "MIT/ASL2"];
#[crate_type = "lib"];

#[deny(non_camel_case_types)];
//#[deny(missing_doc)];

#[feature(macro_rules)];
#[macro_escape];

extern mod extra;

pub mod buffer;
pub mod client;
pub mod common;
pub mod server;
pub mod method;
pub mod headers;
pub mod rfc2616;
#[path = "generated/status.rs"]
pub mod status;  // Getting an error? It's generated; use ``make`` or see the ``Makefile``

/// TODO: submit upstream
pub mod memstream;
<|endoftext|>


<commit_before>//!
//! A convenient structure to store and load settings.
//!
//! It is meant for items that are rarely read and stored, like command line flags or
//! application configuration.
//!
//! knob::Settings expects all values to implement ToStr + FromStr and stores them as
//! strings internally. This allows sideloading of settings through multiple means, e.g.
//! the command line or a simple config file. knob is not meant for structured data. If
//! you want to load such data, store the location of the data as a Knob setting and do
//! the loading parsing yourself.
//!
//! knob is typesafe in a sense that it will return you the type you wanted back if possible and
//! fails otherwise.
//!
//! knob allows you to decorate the Settings structure yourself for convenience (see examples).
//!
//! # Storing and fetching values
//!
//! The following example shows you how to store an ip in knob:
//!
//! ~~~{.rust}
//! extern crate knob;
//! use knob::*;
//! use std::rt::io::net::ip::IpAddr;
//!
//! fn main() {
//!   let settings = Settings::new();
//!   settings.set("ip", "0.0.0.0:4567");
//!   let socket: IpAddr = settings.socket();
//!   assert_eq!(socket.to_str(), ~"0.0.0.0:4567")
//! }
//! ~~~
//!
//! This works the same for IPv6 addresses:
//!
//! ~~~{.rust}
//! fn main() {
//!   let settings = Settings::new();
//!   settings.set("ip", "::0.0.0.1");
//!   let socket: IpAddr = settings.fetch("ip");
//!   assert_eq!(socket.to_str(), ~"::0.0.0.1")
//! }
//! ~~~
//!
//! # Providing your own keys
//!
//! You can use enums as keys, as long as they implement ToStr:
//!
//! ~~~{.rust}
//! enum Keys {
//!   Port,
//!   Ip,
//!   Addr,
//! }
//!
//! fn main() {
//!   let settings = Settings::new();
//!   settings.set(Ip, "::0.0.0.1");
//!   let socket: IpAddr = settings.fetch(Ip);
//!   assert_eq!(socket.to_str(), ~"::0.0.0.1")
//! }
//! ~~~
//!
//! # Registering command line options
//!
//! Knob allows you to register command line options to read from the command line later.
//!
//! The options are getopts option groups.
//!
//! If the loading of the command line args fails, an error will be returned.
//!
//! ~~~{.rust}
//! use extra::getopts::groups::*;
//!
//! fn main() {
//!   let settings = Settings::new();
//!   settings.opt(optopt("p", "port", "the port to bind to", "4000"))
//!   settings.opt(reqopt("e", "environment", "the environment to run in", ""));;
//!   let errors = settings.load_os_args();
//!   if errors.is_some() {
//!     println(settings.usage("Try one of these:"))
//!   }
//! }
//! ~~~
//!
//! # Decorating the settings struct
//!
//! To make matters more convenient, you can implement a decorator
//! to implement your own loading behaviour.
//!
//! ~~~{.rust}
//! enum Keys {
//!   Port,
//!   Ip,
//!   Addr,
//! }
//!
//! pub trait SocketSettings {
//!   fn socket(&self) -> SocketAddr;
//!   fn port(&self) -> u16;
//!   fn ip(&self) -> IpAddr;
//! }
//!
//! impl SocketSettings for Settings {
//!   fn socket(&self) -> SocketAddr {
//!     do self.fetch_with(Addr) |addr| {
//!       match addr {
//!         Some(socket_addr) => { socket_addr },
//!         None => {
//!           let port: u16 = self.port();
//!           let ip: IpAddr = self.ip();
//!           SocketAddr { ip: ip, port: port }
//!         }
//!       }
//!     }
//!   }
//!
//!   fn port(&self) -> u16 {
//!     self.fetch(Port).unwrap_or(8080)
//!   }
//!
//!   fn ip(&self) -> IpAddr {
//!     self.fetch(Ip).unwrap_or(Ipv4Addr(127,0,0,1))
//!   }
//! }
//! enum Keys {
//!   Port,
//!   Ip,
//! }
//!
//! fn main() {
//!   let settings = Settings::new();
//!   settings.set(Ip, "::0.0.0.1");
//!   let socket: IpAddr = settings.ip();
//!   assert_eq!(socket.to_str(), ~"::0.0.0.1")
//! }
//! ~~~
//!
//! knob goes up to 11.

#[crate_id = "knob#1.0.1"];
#[crate_type = "lib"];
#[comment = "A convenient Rust settings system"];
#[license = "MIT"];

extern crate getopts;
extern crate collections;

use collections::hashmap::HashMap;
use std::os;
use std::from_str::FromStr;
use std::to_str::ToStr;

use getopts::{usage,getopts,OptGroup};
use getopts::Fail_;

/// The settings structure we save the options and settings in.
#[deriving(Clone)]
pub struct Settings {
  priv store: HashMap<~str,~str>,
  priv options: ~[OptGroup],
}

impl Settings {
  /// Create a new Settings struct.
  pub fn new() -> Settings {
    Settings { store: HashMap::new(), options: ~[] }
  }

  /// Set a settings key to a value. The value will be serialized.
  pub fn set<A: ToStr, T: ToStr>(&mut self, setting: A, value: T) {
    self.store.swap(setting.to_str(), value.to_str());
  }

  /// Set a value using an Option struct. The value will only be set if the
  /// Some value is given. This way, you can avoid unwrapping the result of a
  /// previous operation by yourself.
  pub fn set_opt<A: ToStr, T: ToStr>(&mut self, setting: A, value: Option<T>) {
    if !value.is_none() {
      self.store.swap(setting.to_str(), value.unwrap().to_str());
    }
  }

  /// Fetch a setting for a key. Fails if the setting is present but could not be
  /// parsed.
  pub fn fetch<A: ToStr, T: FromStr>(&self, setting: A) -> Option<T> {
    match self.store.find(&setting.to_str()) {
      Some(string) => {
        let value = from_str(string.to_owned());
        if value.is_none() {
          fail!("setting could not be parsed:" + setting.to_str())
        }
        value
      },
      None => { None }
    }
  }

  /// Fetch a setting for a key and pass it to given function. The result of the function
  /// will be returned.
  pub fn fetch_with<A: ToStr, T: FromStr>(&self, setting: A, f: |Option<T>| -> T) -> T {
    let value = self.fetch(setting.to_str());
    f(value)
  }

  /// Register a commandline for laster use with load_args.
  ///
  /// Currently, only optopt and reqopt are properly supported.
  pub fn opt(&mut self, opt: OptGroup) {
    self.options.push(opt);
  }

  /// Load the command line argument given by the OS.
  ///
  /// Optionally returns failures.
  pub fn load_os_args(&mut self) -> Option<Fail_> {
    let args = os::args();
    self.load_args(args)
  }

  /// Load a list of command line arguments.
  ///
  /// Automatically sets "knob.progname" to the name of the program.
  ///
  /// Optionally returns failures.
  pub fn load_args(&mut self, args: ~[~str]) -> Option<Fail_> {
    let ref prog_name = args[0];

    self.set("knob.progname", prog_name.clone());

    let matches = match getopts(args.tail(), self.options) {
      Ok(m) => { m }
      Err(fail) => { return Some(fail) }
    };

    let given_options = self.options.clone();
    for opt in given_options.iter() {
      let opt_strings = &[opt.short_name.clone(), opt.long_name.clone()];
      self.set_opt(opt.long_name.clone(), matches.opts_str(opt_strings))
    };
    None
  }

  /// Returns the usage string for the stored OptGroups. Pass `brief`
  /// to have it included.
  pub fn usage(&self, brief: &str) -> ~str {
    usage(brief, self.options)
  }
}

<commit_msg>Use new crate attribute syntax<commit_after>//!
//! A convenient structure to store and load settings.
//!
//! It is meant for items that are rarely read and stored, like command line flags or
//! application configuration.
//!
//! knob::Settings expects all values to implement ToStr + FromStr and stores them as
//! strings internally. This allows sideloading of settings through multiple means, e.g.
//! the command line or a simple config file. knob is not meant for structured data. If
//! you want to load such data, store the location of the data as a Knob setting and do
//! the loading parsing yourself.
//!
//! knob is typesafe in a sense that it will return you the type you wanted back if possible and
//! fails otherwise.
//!
//! knob allows you to decorate the Settings structure yourself for convenience (see examples).
//!
//! # Storing and fetching values
//!
//! The following example shows you how to store an ip in knob:
//!
//! ~~~{.rust}
//! extern crate knob;
//! use knob::*;
//! use std::rt::io::net::ip::IpAddr;
//!
//! fn main() {
//!   let settings = Settings::new();
//!   settings.set("ip", "0.0.0.0:4567");
//!   let socket: IpAddr = settings.socket();
//!   assert_eq!(socket.to_str(), ~"0.0.0.0:4567")
//! }
//! ~~~
//!
//! This works the same for IPv6 addresses:
//!
//! ~~~{.rust}
//! fn main() {
//!   let settings = Settings::new();
//!   settings.set("ip", "::0.0.0.1");
//!   let socket: IpAddr = settings.fetch("ip");
//!   assert_eq!(socket.to_str(), ~"::0.0.0.1")
//! }
//! ~~~
//!
//! # Providing your own keys
//!
//! You can use enums as keys, as long as they implement ToStr:
//!
//! ~~~{.rust}
//! enum Keys {
//!   Port,
//!   Ip,
//!   Addr,
//! }
//!
//! fn main() {
//!   let settings = Settings::new();
//!   settings.set(Ip, "::0.0.0.1");
//!   let socket: IpAddr = settings.fetch(Ip);
//!   assert_eq!(socket.to_str(), ~"::0.0.0.1")
//! }
//! ~~~
//!
//! # Registering command line options
//!
//! Knob allows you to register command line options to read from the command line later.
//!
//! The options are getopts option groups.
//!
//! If the loading of the command line args fails, an error will be returned.
//!
//! ~~~{.rust}
//! use extra::getopts::groups::*;
//!
//! fn main() {
//!   let settings = Settings::new();
//!   settings.opt(optopt("p", "port", "the port to bind to", "4000"))
//!   settings.opt(reqopt("e", "environment", "the environment to run in", ""));;
//!   let errors = settings.load_os_args();
//!   if errors.is_some() {
//!     println(settings.usage("Try one of these:"))
//!   }
//! }
//! ~~~
//!
//! # Decorating the settings struct
//!
//! To make matters more convenient, you can implement a decorator
//! to implement your own loading behaviour.
//!
//! ~~~{.rust}
//! enum Keys {
//!   Port,
//!   Ip,
//!   Addr,
//! }
//!
//! pub trait SocketSettings {
//!   fn socket(&self) -> SocketAddr;
//!   fn port(&self) -> u16;
//!   fn ip(&self) -> IpAddr;
//! }
//!
//! impl SocketSettings for Settings {
//!   fn socket(&self) -> SocketAddr {
//!     do self.fetch_with(Addr) |addr| {
//!       match addr {
//!         Some(socket_addr) => { socket_addr },
//!         None => {
//!           let port: u16 = self.port();
//!           let ip: IpAddr = self.ip();
//!           SocketAddr { ip: ip, port: port }
//!         }
//!       }
//!     }
//!   }
//!
//!   fn port(&self) -> u16 {
//!     self.fetch(Port).unwrap_or(8080)
//!   }
//!
//!   fn ip(&self) -> IpAddr {
//!     self.fetch(Ip).unwrap_or(Ipv4Addr(127,0,0,1))
//!   }
//! }
//! enum Keys {
//!   Port,
//!   Ip,
//! }
//!
//! fn main() {
//!   let settings = Settings::new();
//!   settings.set(Ip, "::0.0.0.1");
//!   let socket: IpAddr = settings.ip();
//!   assert_eq!(socket.to_str(), ~"::0.0.0.1")
//! }
//! ~~~
//!
//! knob goes up to 11.

#![crate_id = "knob#1.0.1"]
#![crate_type = "lib"]
#![comment = "A convenient Rust settings system"]
#![license = "MIT"]

extern crate getopts;
extern crate collections;

use collections::hashmap::HashMap;
use std::os;
use std::from_str::FromStr;
use std::to_str::ToStr;

use getopts::{usage,getopts,OptGroup};
use getopts::Fail_;

/// The settings structure we save the options and settings in.
#[deriving(Clone)]
pub struct Settings {
  priv store: HashMap<~str,~str>,
  priv options: ~[OptGroup],
}

impl Settings {
  /// Create a new Settings struct.
  pub fn new() -> Settings {
    Settings { store: HashMap::new(), options: ~[] }
  }

  /// Set a settings key to a value. The value will be serialized.
  pub fn set<A: ToStr, T: ToStr>(&mut self, setting: A, value: T) {
    self.store.swap(setting.to_str(), value.to_str());
  }

  /// Set a value using an Option struct. The value will only be set if the
  /// Some value is given. This way, you can avoid unwrapping the result of a
  /// previous operation by yourself.
  pub fn set_opt<A: ToStr, T: ToStr>(&mut self, setting: A, value: Option<T>) {
    if !value.is_none() {
      self.store.swap(setting.to_str(), value.unwrap().to_str());
    }
  }

  /// Fetch a setting for a key. Fails if the setting is present but could not be
  /// parsed.
  pub fn fetch<A: ToStr, T: FromStr>(&self, setting: A) -> Option<T> {
    match self.store.find(&setting.to_str()) {
      Some(string) => {
        let value = from_str(string.to_owned());
        if value.is_none() {
          fail!("setting could not be parsed:" + setting.to_str())
        }
        value
      },
      None => { None }
    }
  }

  /// Fetch a setting for a key and pass it to given function. The result of the function
  /// will be returned.
  pub fn fetch_with<A: ToStr, T: FromStr>(&self, setting: A, f: |Option<T>| -> T) -> T {
    let value = self.fetch(setting.to_str());
    f(value)
  }

  /// Register a commandline for laster use with load_args.
  ///
  /// Currently, only optopt and reqopt are properly supported.
  pub fn opt(&mut self, opt: OptGroup) {
    self.options.push(opt);
  }

  /// Load the command line argument given by the OS.
  ///
  /// Optionally returns failures.
  pub fn load_os_args(&mut self) -> Option<Fail_> {
    let args = os::args();
    self.load_args(args)
  }

  /// Load a list of command line arguments.
  ///
  /// Automatically sets "knob.progname" to the name of the program.
  ///
  /// Optionally returns failures.
  pub fn load_args(&mut self, args: ~[~str]) -> Option<Fail_> {
    let ref prog_name = args[0];

    self.set("knob.progname", prog_name.clone());

    let matches = match getopts(args.tail(), self.options) {
      Ok(m) => { m }
      Err(fail) => { return Some(fail) }
    };

    let given_options = self.options.clone();
    for opt in given_options.iter() {
      let opt_strings = &[opt.short_name.clone(), opt.long_name.clone()];
      self.set_opt(opt.long_name.clone(), matches.opts_str(opt_strings))
    };
    None
  }

  /// Returns the usage string for the stored OptGroups. Pass `brief`
  /// to have it included.
  pub fn usage(&self, brief: &str) -> ~str {
    usage(brief, self.options)
  }
}

<|endoftext|>


<commit_before>use std::{
    io::{self, ErrorKind},
    net::SocketAddr,
    sync::Arc,
    time::Duration,
};

use byte_string::ByteStr;
use kcp::{Error as KcpError, KcpResult};
use log::{debug, error, trace};
use tokio::{
    net::{ToSocketAddrs, UdpSocket},
    sync::mpsc,
    task::JoinHandle,
    time,
};

use crate::{config::KcpConfig, session::KcpSessionManager, stream::KcpStream};

pub struct KcpListener {
    udp: Arc<UdpSocket>,
    accept_rx: mpsc::Receiver<(KcpStream, SocketAddr)>,
    task_watcher: JoinHandle<()>,
}

impl Drop for KcpListener {
    fn drop(&mut self) {
        self.task_watcher.abort();
    }
}

impl KcpListener {
    /// Create an `KcpListener` bound to `addr`
    pub async fn bind<A: ToSocketAddrs>(config: KcpConfig, addr: A) -> KcpResult<KcpListener> {
        let udp = UdpSocket::bind(addr).await?;
        let udp = Arc::new(udp);
        let server_udp = udp.clone();

        let (accept_tx, accept_rx) = mpsc::channel(1024 /* backlogs */);
        let task_watcher = tokio::spawn(async move {
            let (close_tx, mut close_rx) = mpsc::channel(64);

            let mut sessions = KcpSessionManager::new();
            let mut packet_buffer = [0u8; 65536];
            loop {
                tokio::select! {
                    peer_addr = close_rx.recv() => {
                        let peer_addr = peer_addr.expect("close_tx closed unexpectly");
                        sessions.close_peer(peer_addr);
                        trace!("session peer_addr: {} removed", peer_addr);
                    }

                    recv_res = udp.recv_from(&mut packet_buffer) => {
                        match recv_res {
                            Err(err) => {
                                error!("udp.recv_from failed, error: {}", err);
                                time::sleep(Duration::from_secs(1)).await;
                            }
                            Ok((n, peer_addr)) => {
                                let packet = &mut packet_buffer[..n];

                                log::trace!("received peer: {}, {:?}", peer_addr, ByteStr::new(packet));

                                let mut conv = kcp::get_conv(packet);
                                if conv == 0 {
                                    // Allocate a conv for client.
                                    conv = sessions.alloc_conv();
                                    debug!("allocate {} conv for peer: {}", conv, peer_addr);

                                    kcp::set_conv(packet, conv);
                                }

                                let sn = kcp::get_sn(packet);

                                let session = match sessions.get_or_create(&config, conv, sn, &udp, peer_addr, &close_tx).await {
                                    Ok((s, created)) => {
                                        if created {
                                            // Created a new session, constructed a new accepted client
                                            let stream = KcpStream::with_session(s.clone());
                                            if let Err(..) = accept_tx.try_send((stream, peer_addr)) {
                                                debug!("failed to create accepted stream due to channel failure");

                                                // remove it from session
                                                sessions.close_peer(peer_addr);
                                                continue;
                                            }
                                        } else {
                                            let session_conv = s.conv().await;
                                            if session_conv != conv {
                                                debug!("received peer: {} with conv: {} not match with session conv: {}",
                                                       peer_addr,
                                                       conv,
                                                       session_conv);
                                                continue;
                                            }
                                        }

                                        s
                                    },
                                    Err(err) => {
                                        error!("failed to create session, error: {}, peer: {}, conv: {}", err, peer_addr, conv);
                                        continue;
                                    }
                                };

                                // let mut kcp = session.kcp_socket().lock().await;
                                // if let Err(err) = kcp.input(packet) {
                                //     error!("kcp.input failed, peer: {}, conv: {}, error: {}, packet: {:?}", peer_addr, conv, err, ByteStr::new(packet));
                                // }
                                session.input(packet).await;
                            }
                        }
                    }
                }
            }
        });

        Ok(KcpListener {
            udp: server_udp,
            accept_rx,
            task_watcher,
        })
    }

    /// Accept a new connected `KcpStream`
    pub async fn accept(&mut self) -> KcpResult<(KcpStream, SocketAddr)> {
        match self.accept_rx.recv().await {
            Some(s) => Ok(s),
            None => Err(KcpError::IoError(io::Error::new(
                ErrorKind::Other,
                "accept channel closed unexpectly",
            ))),
        }
    }

    /// Get the local address of the underlying socket
    pub fn local_addr(&self) -> io::Result<SocketAddr> {
        self.udp.local_addr()
    }
}

#[cfg(unix)]
impl std::os::unix::io::AsRawFd for KcpListener {
    fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
        self.udp.as_raw_fd()
    }
}

#[cfg(windows)]
impl std::os::windows::io::AsRawSocket for KcpListener {
    fn as_raw_socket(&self) -> std::os::windows::prelude::RawSocket {
        self.udp.as_raw_socket()
    }
}

#[cfg(test)]
mod test {
    use super::KcpListener;
    use crate::{config::KcpConfig, stream::KcpStream};
    use futures::future;
    use tokio::io::{AsyncReadExt, AsyncWriteExt};

    #[tokio::test]
    async fn multi_echo() {
        let _ = env_logger::try_init();

        let config = KcpConfig::default();

        let mut listener = KcpListener::bind(config.clone(), "127.0.0.1:0").await.unwrap();
        let server_addr = listener.local_addr().unwrap();

        tokio::spawn(async move {
            loop {
                let (mut stream, _) = listener.accept().await.unwrap();

                tokio::spawn(async move {
                    let mut buffer = [0u8; 8192];
                    while let Ok(n) = stream.read(&mut buffer).await {
                        if n == 0 {
                            break;
                        }

                        let data = &buffer[..n];
                        stream.write_all(data).await.unwrap();
                        stream.flush().await.unwrap();
                    }
                });
            }
        });

        let mut vfut = Vec::new();

        for _ in 0..100 {
            vfut.push(async move {
                let mut stream = KcpStream::connect(&config, server_addr).await.unwrap();

                for _ in 0..20 {
                    const SEND_BUFFER: &[u8] = b"HELLO WORLD";
                    stream.write_all(SEND_BUFFER).await.unwrap();
                    stream.flush().await.unwrap();

                    let mut buffer = [0u8; 1024];
                    let n = stream.recv(&mut buffer).await.unwrap();
                    assert_eq!(SEND_BUFFER, &buffer[..n]);
                }
            });
        }

        future::join_all(vfut).await;
    }
}
<commit_msg>fix: remove unnecessary clone<commit_after>use std::{
    io::{self, ErrorKind},
    net::SocketAddr,
    sync::Arc,
    time::Duration,
};

use byte_string::ByteStr;
use kcp::{Error as KcpError, KcpResult};
use log::{debug, error, trace};
use tokio::{
    net::{ToSocketAddrs, UdpSocket},
    sync::mpsc,
    task::JoinHandle,
    time,
};

use crate::{config::KcpConfig, session::KcpSessionManager, stream::KcpStream};

pub struct KcpListener {
    udp: Arc<UdpSocket>,
    accept_rx: mpsc::Receiver<(KcpStream, SocketAddr)>,
    task_watcher: JoinHandle<()>,
}

impl Drop for KcpListener {
    fn drop(&mut self) {
        self.task_watcher.abort();
    }
}

impl KcpListener {
    /// Create an `KcpListener` bound to `addr`
    pub async fn bind<A: ToSocketAddrs>(config: KcpConfig, addr: A) -> KcpResult<KcpListener> {
        let udp = UdpSocket::bind(addr).await?;
        let udp = Arc::new(udp);
        let server_udp = udp.clone();

        let (accept_tx, accept_rx) = mpsc::channel(1024 /* backlogs */);
        let task_watcher = tokio::spawn(async move {
            let (close_tx, mut close_rx) = mpsc::channel(64);

            let mut sessions = KcpSessionManager::new();
            let mut packet_buffer = [0u8; 65536];
            loop {
                tokio::select! {
                    peer_addr = close_rx.recv() => {
                        let peer_addr = peer_addr.expect("close_tx closed unexpectly");
                        sessions.close_peer(peer_addr);
                        trace!("session peer_addr: {} removed", peer_addr);
                    }

                    recv_res = udp.recv_from(&mut packet_buffer) => {
                        match recv_res {
                            Err(err) => {
                                error!("udp.recv_from failed, error: {}", err);
                                time::sleep(Duration::from_secs(1)).await;
                            }
                            Ok((n, peer_addr)) => {
                                let packet = &mut packet_buffer[..n];

                                log::trace!("received peer: {}, {:?}", peer_addr, ByteStr::new(packet));

                                let mut conv = kcp::get_conv(packet);
                                if conv == 0 {
                                    // Allocate a conv for client.
                                    conv = sessions.alloc_conv();
                                    debug!("allocate {} conv for peer: {}", conv, peer_addr);

                                    kcp::set_conv(packet, conv);
                                }

                                let sn = kcp::get_sn(packet);

                                let session = match sessions.get_or_create(&config, conv, sn, &udp, peer_addr, &close_tx).await {
                                    Ok((s, created)) => {
                                        if created {
                                            // Created a new session, constructed a new accepted client
                                            let stream = KcpStream::with_session(s.clone());
                                            if let Err(..) = accept_tx.try_send((stream, peer_addr)) {
                                                debug!("failed to create accepted stream due to channel failure");

                                                // remove it from session
                                                sessions.close_peer(peer_addr);
                                                continue;
                                            }
                                        } else {
                                            let session_conv = s.conv().await;
                                            if session_conv != conv {
                                                debug!("received peer: {} with conv: {} not match with session conv: {}",
                                                       peer_addr,
                                                       conv,
                                                       session_conv);
                                                continue;
                                            }
                                        }

                                        s
                                    },
                                    Err(err) => {
                                        error!("failed to create session, error: {}, peer: {}, conv: {}", err, peer_addr, conv);
                                        continue;
                                    }
                                };

                                // let mut kcp = session.kcp_socket().lock().await;
                                // if let Err(err) = kcp.input(packet) {
                                //     error!("kcp.input failed, peer: {}, conv: {}, error: {}, packet: {:?}", peer_addr, conv, err, ByteStr::new(packet));
                                // }
                                session.input(packet).await;
                            }
                        }
                    }
                }
            }
        });

        Ok(KcpListener {
            udp: server_udp,
            accept_rx,
            task_watcher,
        })
    }

    /// Accept a new connected `KcpStream`
    pub async fn accept(&mut self) -> KcpResult<(KcpStream, SocketAddr)> {
        match self.accept_rx.recv().await {
            Some(s) => Ok(s),
            None => Err(KcpError::IoError(io::Error::new(
                ErrorKind::Other,
                "accept channel closed unexpectly",
            ))),
        }
    }

    /// Get the local address of the underlying socket
    pub fn local_addr(&self) -> io::Result<SocketAddr> {
        self.udp.local_addr()
    }
}

#[cfg(unix)]
impl std::os::unix::io::AsRawFd for KcpListener {
    fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
        self.udp.as_raw_fd()
    }
}

#[cfg(windows)]
impl std::os::windows::io::AsRawSocket for KcpListener {
    fn as_raw_socket(&self) -> std::os::windows::prelude::RawSocket {
        self.udp.as_raw_socket()
    }
}

#[cfg(test)]
mod test {
    use super::KcpListener;
    use crate::{config::KcpConfig, stream::KcpStream};
    use futures::future;
    use tokio::io::{AsyncReadExt, AsyncWriteExt};

    #[tokio::test]
    async fn multi_echo() {
        let _ = env_logger::try_init();

        let config = KcpConfig::default();

        let mut listener = KcpListener::bind(config, "127.0.0.1:0").await.unwrap();
        let server_addr = listener.local_addr().unwrap();

        tokio::spawn(async move {
            loop {
                let (mut stream, _) = listener.accept().await.unwrap();

                tokio::spawn(async move {
                    let mut buffer = [0u8; 8192];
                    while let Ok(n) = stream.read(&mut buffer).await {
                        if n == 0 {
                            break;
                        }

                        let data = &buffer[..n];
                        stream.write_all(data).await.unwrap();
                        stream.flush().await.unwrap();
                    }
                });
            }
        });

        let mut vfut = Vec::new();

        for _ in 0..100 {
            vfut.push(async move {
                let mut stream = KcpStream::connect(&config, server_addr).await.unwrap();

                for _ in 0..20 {
                    const SEND_BUFFER: &[u8] = b"HELLO WORLD";
                    stream.write_all(SEND_BUFFER).await.unwrap();
                    stream.flush().await.unwrap();

                    let mut buffer = [0u8; 1024];
                    let n = stream.recv(&mut buffer).await.unwrap();
                    assert_eq!(SEND_BUFFER, &buffer[..n]);
                }
            });
        }

        future::join_all(vfut).await;
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::fs::File;
use std::cmp;
use tar::Archive;
use spiral::Spiral;
use dfa::{Dfa, DfaState};
use cell::Cell;
use field::Field;

struct Move {
  x: u32,
  y: u32,
  p: f64 // probability
}

struct Pattern {
  p: f64, // priority
  width: u32,
  height: u32,
  moves: Vec<Move>
}

pub struct Patterns {
  min_size: u32,
  dfa: Dfa,
  patterns: Vec<Pattern>
}

impl Patterns {
  fn read_header<T: BufRead>(input: &mut T, s: &mut String) -> (u32, u32, u32, f64) {
    s.clear();
    input.read_line(s).ok();
    s.pop();
    let mut split = s.split(' ').fuse();
    let width = u32::from_str(split.next().expect("Invalid pattern format: expected width.")).expect("Invalid pattern format: width must be u32."); //TODO: validate all this.
    let height = u32::from_str(split.next().expect("Invalid pattern format: expected height.")).expect("Invalid pattern format: height must be u32.");
    let moves_count = u32::from_str(split.next().expect("Invalid pattern format: expected moves count.")).expect("Invalid pattern format: moves count must be u32.");
    let priority = f64::from_str(split.next().expect("Invalid pattern format: expected priority.")).expect("Invalid pattern format: priority must be f64.");
    (width, height, moves_count, priority)
  }

  fn read_pattern<T: BufRead>(input: &mut T, s: &mut String, width: u32, height: u32) {
    s.clear();
    for y in 0 .. height {
      input.read_line(s).ok(); //TODO: check sizes.
      s.pop();
    }
  }

  fn read_moves<T: BufRead>(input: &mut T, s: &mut String, moves_count: u32) -> Vec<Move> {
    let mut moves = Vec::with_capacity(moves_count as usize);
    for _ in 0 .. moves_count {
      s.clear();
      input.read_line(s).ok();
      s.pop();
      let mut split = s.split(' ').fuse();
      let x = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let y = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let p = f64::from_str(split.next().expect("Invalid pattern format: expected probability.")).expect("Invalid pattern format: probability must be f64.");
      moves.push(Move {
        x: x,
        y: y,
        p: p
      });
    }
    moves
  }

  fn covering_spiral_length(side_of_square: u32) -> u32 {
    let x = side_of_square / 2 + 1;
    let y = (1 - side_of_square % 2) * side_of_square * 2;
    (8 * x - 13) * x + 6 - y
  }

  fn build_dfa(width: u32, height: u32, pattern: usize, s: &str) -> Dfa { //TODO: different color, rotations, reflections.
    let center_x = (width - 1) / 2;
    let center_y = (height - 1) / 2;
    let spiral_length = Patterns::covering_spiral_length(cmp::max(width, height)) as usize;
    let mut states = Vec::with_capacity(spiral_length + 2);
    let fs = spiral_length;
    let nfs = spiral_length + 1; // Not found state.
    let mut i = 0;
    for (shift_x, shift_y) in Spiral::new().into_iter().take(spiral_length) {
      i += 1;
      let x = center_x as i32 + shift_x;
      let y = center_y as i32 + shift_y;
      let state = if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
        let pos = y as u32 * width + x as u32;
        match s.char_at(pos as usize) {
          '.' => DfaState::new(i, nfs, nfs, nfs, false, HashSet::with_capacity(0)),
          '?' => DfaState::new(i, i, i, i, false, HashSet::with_capacity(0)),
          'R' => DfaState::new(nfs, i, nfs, nfs, false, HashSet::with_capacity(0)),
          'B' => DfaState::new(nfs, nfs, i, nfs, false, HashSet::with_capacity(0)),
          'r' => DfaState::new(i, i, nfs, nfs, false, HashSet::with_capacity(0)),
          'b' => DfaState::new(i, nfs, i, nfs, false, HashSet::with_capacity(0)),
          '*' => DfaState::new(nfs, nfs, nfs, i, false, HashSet::with_capacity(0)),
          c   => panic!("Invalid character in pattern: {}", c)
        }
      } else {
        DfaState::new(i, i, i, i, false, HashSet::with_capacity(0))
      };
      states.push(state);
    }
    let mut patterns = HashSet::with_capacity(1);
    patterns.insert(pattern);
    states.push(DfaState::new(fs, fs, fs, fs, true, patterns));
    states.push(DfaState::new(nfs, nfs, nfs, nfs, true, HashSet::with_capacity(0)));
    Dfa::new(states)
  }

  pub fn load(file: File) -> Patterns {
    let archive = Archive::new(file);
    let mut s = String::new();
    let mut patterns = Vec::new();
    let mut iter = archive.files().expect("Reading of tar archive is failed.").into_iter().map(|file| file.expect("Reading of file in tar archive is failed."));
    let mut dfa = Dfa::empty();
    let mut min_size = u32::max_value();
    for file in iter {
      let mut input = BufReader::new(file);
      let (width, height, moves_count, priority) = Patterns::read_header(&mut input, &mut s);
      if width < min_size {
        min_size = width;
      }
      if height < min_size {
        min_size = height;
      }
      Patterns::read_pattern(&mut input, &mut s, width, height);
      let cur_dfa = Patterns::build_dfa(width, height, patterns.len(), &s);
      dfa = dfa.product(&cur_dfa);
      dfa.delete_non_reachable();
      let moves = Patterns::read_moves(&mut input, &mut s, moves_count);
      patterns.push(Pattern {
        p: priority,
        width: width,
        height: height,
        moves: moves
      });
    }
    dfa.minimize(); //TODO: does it work?
    Patterns {
      min_size: min_size,
      dfa: dfa,
      patterns: patterns
    }
  }

  pub fn find(&self, field: &Field, first_match: bool) -> Vec<(u32, u32, f64)> {
    if self.dfa.is_empty() {
      return Vec::with_capacity(0);
    }
    let mut priorities_sum = 0f64;
    let mut moves_count = 0usize;
    let mut matched = Vec::new();
    let left_border = (self.min_size - 1) / 2;
    let right_border = self.min_size / 2;
    for y in left_border .. field.height() - right_border {
      for x in left_border .. field.width() - right_border {
        let patterns = self.dfa.run(&mut Spiral::new().into_iter().map(|(shift_x, shift_y)| {
          let cur_x = x as i32 + shift_x;
          let cur_y = y as i32 + shift_y;
          if cur_x >= 0 && cur_x < field.width() as i32 && cur_y >= 0 && cur_y < field.height() as i32 {
            let pos = field.to_pos(cur_x as u32, cur_y as u32);
            field.cell(pos)
          } else {
            Cell::new(true)
          }
        }), first_match);
        for &pattern_number in patterns {
          let pattern = &self.patterns[pattern_number];
          priorities_sum += pattern.p;
          moves_count += pattern.moves.len();
          matched.push((pattern_number, x, y));
        }
      }
    }
    let mut result = Vec::with_capacity(moves_count);
    for (pattern_number, center_x, center_y) in matched {
      let pattern = &self.patterns[pattern_number];
      for &Move { x, y, p: probability } in &pattern.moves {
        let move_x = center_x - (pattern.width - 1) / 2 + x;
        let move_y = center_y - (pattern.height - 1) / 2 + y;
        result.push((move_x, move_y, probability * pattern.p / priorities_sum));
      }
    }
    result
  }
}
<commit_msg>Change pattern symbol.<commit_after>use std::collections::HashSet;
use std::io::{BufReader, BufRead};
use std::str::FromStr;
use std::fs::File;
use std::cmp;
use tar::Archive;
use spiral::Spiral;
use dfa::{Dfa, DfaState};
use cell::Cell;
use field::Field;

struct Move {
  x: u32,
  y: u32,
  p: f64 // probability
}

struct Pattern {
  p: f64, // priority
  width: u32,
  height: u32,
  moves: Vec<Move>
}

pub struct Patterns {
  min_size: u32,
  dfa: Dfa,
  patterns: Vec<Pattern>
}

impl Patterns {
  fn read_header<T: BufRead>(input: &mut T, s: &mut String) -> (u32, u32, u32, f64) {
    s.clear();
    input.read_line(s).ok();
    s.pop();
    let mut split = s.split(' ').fuse();
    let width = u32::from_str(split.next().expect("Invalid pattern format: expected width.")).expect("Invalid pattern format: width must be u32."); //TODO: validate all this.
    let height = u32::from_str(split.next().expect("Invalid pattern format: expected height.")).expect("Invalid pattern format: height must be u32.");
    let moves_count = u32::from_str(split.next().expect("Invalid pattern format: expected moves count.")).expect("Invalid pattern format: moves count must be u32.");
    let priority = f64::from_str(split.next().expect("Invalid pattern format: expected priority.")).expect("Invalid pattern format: priority must be f64.");
    (width, height, moves_count, priority)
  }

  fn read_pattern<T: BufRead>(input: &mut T, s: &mut String, width: u32, height: u32) {
    s.clear();
    for y in 0 .. height {
      input.read_line(s).ok(); //TODO: check sizes.
      s.pop();
    }
  }

  fn read_moves<T: BufRead>(input: &mut T, s: &mut String, moves_count: u32) -> Vec<Move> {
    let mut moves = Vec::with_capacity(moves_count as usize);
    for _ in 0 .. moves_count {
      s.clear();
      input.read_line(s).ok();
      s.pop();
      let mut split = s.split(' ').fuse();
      let x = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let y = u32::from_str(split.next().expect("Invalid pattern format: expected x coordinate.")).expect("Invalid pattern format: x coordinate must be u32.");
      let p = f64::from_str(split.next().expect("Invalid pattern format: expected probability.")).expect("Invalid pattern format: probability must be f64.");
      moves.push(Move {
        x: x,
        y: y,
        p: p
      });
    }
    moves
  }

  fn covering_spiral_length(side_of_square: u32) -> u32 {
    let x = side_of_square / 2 + 1;
    let y = (1 - side_of_square % 2) * side_of_square * 2;
    (8 * x - 13) * x + 6 - y
  }

  fn build_dfa(width: u32, height: u32, pattern: usize, s: &str) -> Dfa { //TODO: different color, rotations, reflections.
    let center_x = (width - 1) / 2;
    let center_y = (height - 1) / 2;
    let spiral_length = Patterns::covering_spiral_length(cmp::max(width, height)) as usize;
    let mut states = Vec::with_capacity(spiral_length + 2);
    let fs = spiral_length;
    let nfs = spiral_length + 1; // Not found state.
    let mut i = 0;
    for (shift_x, shift_y) in Spiral::new().into_iter().take(spiral_length) {
      i += 1;
      let x = center_x as i32 + shift_x;
      let y = center_y as i32 + shift_y;
      let state = if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
        let pos = y as u32 * width + x as u32;
        match s.char_at(pos as usize) {
          '.' => DfaState::new(i, nfs, nfs, nfs, false, HashSet::with_capacity(0)),
          '?' => DfaState::new(i, i, i, i, false, HashSet::with_capacity(0)),
          'R' => DfaState::new(nfs, i, nfs, nfs, false, HashSet::with_capacity(0)),
          'B' => DfaState::new(nfs, nfs, i, nfs, false, HashSet::with_capacity(0)),
          'r' => DfaState::new(i, i, nfs, nfs, false, HashSet::with_capacity(0)),
          'b' => DfaState::new(i, nfs, i, nfs, false, HashSet::with_capacity(0)),
          '#' => DfaState::new(nfs, nfs, nfs, i, false, HashSet::with_capacity(0)),
          c   => panic!("Invalid character in pattern: {}", c)
        }
      } else {
        DfaState::new(i, i, i, i, false, HashSet::with_capacity(0))
      };
      states.push(state);
    }
    let mut patterns = HashSet::with_capacity(1);
    patterns.insert(pattern);
    states.push(DfaState::new(fs, fs, fs, fs, true, patterns));
    states.push(DfaState::new(nfs, nfs, nfs, nfs, true, HashSet::with_capacity(0)));
    Dfa::new(states)
  }

  pub fn load(file: File) -> Patterns {
    let archive = Archive::new(file);
    let mut s = String::new();
    let mut patterns = Vec::new();
    let mut iter = archive.files().expect("Reading of tar archive is failed.").into_iter().map(|file| file.expect("Reading of file in tar archive is failed."));
    let mut dfa = Dfa::empty();
    let mut min_size = u32::max_value();
    for file in iter {
      let mut input = BufReader::new(file);
      let (width, height, moves_count, priority) = Patterns::read_header(&mut input, &mut s);
      if width < min_size {
        min_size = width;
      }
      if height < min_size {
        min_size = height;
      }
      Patterns::read_pattern(&mut input, &mut s, width, height);
      let cur_dfa = Patterns::build_dfa(width, height, patterns.len(), &s);
      dfa = dfa.product(&cur_dfa);
      dfa.delete_non_reachable();
      let moves = Patterns::read_moves(&mut input, &mut s, moves_count);
      patterns.push(Pattern {
        p: priority,
        width: width,
        height: height,
        moves: moves
      });
    }
    dfa.minimize(); //TODO: does it work?
    Patterns {
      min_size: min_size,
      dfa: dfa,
      patterns: patterns
    }
  }

  pub fn find(&self, field: &Field, first_match: bool) -> Vec<(u32, u32, f64)> {
    if self.dfa.is_empty() {
      return Vec::with_capacity(0);
    }
    let mut priorities_sum = 0f64;
    let mut moves_count = 0usize;
    let mut matched = Vec::new();
    let left_border = (self.min_size - 1) / 2;
    let right_border = self.min_size / 2;
    for y in left_border .. field.height() - right_border {
      for x in left_border .. field.width() - right_border {
        let patterns = self.dfa.run(&mut Spiral::new().into_iter().map(|(shift_x, shift_y)| {
          let cur_x = x as i32 + shift_x;
          let cur_y = y as i32 + shift_y;
          if cur_x >= 0 && cur_x < field.width() as i32 && cur_y >= 0 && cur_y < field.height() as i32 {
            let pos = field.to_pos(cur_x as u32, cur_y as u32);
            field.cell(pos)
          } else {
            Cell::new(true)
          }
        }), first_match);
        for &pattern_number in patterns {
          let pattern = &self.patterns[pattern_number];
          priorities_sum += pattern.p;
          moves_count += pattern.moves.len();
          matched.push((pattern_number, x, y));
        }
      }
    }
    let mut result = Vec::with_capacity(moves_count);
    for (pattern_number, center_x, center_y) in matched {
      let pattern = &self.patterns[pattern_number];
      for &Move { x, y, p: probability } in &pattern.moves {
        let move_x = center_x - (pattern.width - 1) / 2 + x;
        let move_y = center_y - (pattern.height - 1) / 2 + y;
        result.push((move_x, move_y, probability * pattern.p / priorities_sum));
      }
    }
    result
  }
}
<|endoftext|>


<commit_before>extern crate tempfile;
extern crate zmq;

use std::collections::HashMap;
use std::io::Read;
use std::sync::Arc;
use std::sync::Mutex;
use time;

use backend;
use dispatcher::server;
use helpers;
use helpers::{NewTaskMessage, TaskProgress, TaskReport, TaskStatus};
use models::Service;
use std::error::Error;
use zmq::SNDMORE;

/// Specifies the binding and operation parameters for a ZMQ ventilator component
pub struct Ventilator {
  /// port to listen on
  pub port: usize,
  /// the size of the dispatch queue
  /// (also the batch size for Task store queue requests)
  pub queue_size: usize,
  /// size of an individual message chunk sent via zeromq
  /// (keep this small to avoid large RAM use, increase to reduce network bandwidth)
  pub message_size: usize,
  /// address for the Task store postgres endpoint
  pub backend_address: String,
}

impl Ventilator {
  /// Starts a new dispatch `Server` (ZMQ Ventilator), to serve tasks to processing workers.
  /// The ventilator shares state with other manager threads via queues for tasks in progress,
  /// as well as a queue for completed tasks pending persisting to disk.
  /// A job limit can be provided as a termination condition for the sink server.
  pub fn start(
    &self,
    services_arc: Arc<Mutex<HashMap<String, Option<Service>>>>,
    progress_queue_arc: Arc<Mutex<HashMap<i64, TaskProgress>>>,
    done_queue_arc: Arc<Mutex<Vec<TaskReport>>>,
    job_limit: Option<usize>,
  ) -> Result<(), Box<Error>>
  {
    // We have a Ventilator-exclusive "queues" stack for tasks to be dispatched
    let mut queues: HashMap<String, Vec<TaskProgress>> = HashMap::new();
    // Assuming this is the only And tidy up the postgres tasks:
    let backend = backend::from_address(&self.backend_address);
    backend.clear_limbo_tasks()?;
    // Ok, let's bind to a port and start broadcasting
    let context = zmq::Context::new();
    let ventilator = context.socket(zmq::ROUTER)?;
    let port_str = self.port.to_string();
    let address = "tcp://*:".to_string() + &port_str;
    assert!(ventilator.bind(&address).is_ok());
    let mut source_job_count: usize = 0;

    loop {
      let mut msg = zmq::Message::new()?;
      let mut identity = zmq::Message::new()?;
      ventilator.recv(&mut identity, 0)?;
      ventilator.recv(&mut msg, 0)?;
      let service_name = msg.as_str().unwrap_or_default().to_string();
      // println!("Task requested for service: {}", service_name.clone());
      let request_time = time::get_time();
      source_job_count += 1;

      let mut dispatched_task_opt: Option<TaskProgress> = None;
      // Requests for unknown service names will be silently ignored.
      if let Some(service) = server::get_sync_service(&service_name, &services_arc, &backend) {
        if !queues.contains_key(&service_name) {
          queues.insert(service_name.clone(), Vec::new());
        }
        let mut task_queue: &mut Vec<TaskProgress> = queues
          .get_mut(&service_name)
          .unwrap_or_else(|| panic!("Could not obtain queue mutex lock in main ventilator loop"));
        if task_queue.is_empty() {
          println!(
            "-- No tasks in task queue for service {:?}, fetching up to {:?} more from backend...",
            service_name, self.queue_size
          );
          // Refetch a new batch of tasks
          let now = time::get_time().sec;
          let fetched_tasks = backend
            .fetch_tasks(&service, self.queue_size)
            .unwrap_or(Vec::new());
          task_queue.extend(fetched_tasks.into_iter().map(|task| TaskProgress {
            task,
            created_at: now,
            retries: 0,
          }));

          // This is a good time to also take care that none of the old tasks are dead in the
          // progress queue since the re-fetch happens infrequently, and directly
          // implies the progress queue will grow
          let expired_tasks = server::timeout_progress_tasks(&progress_queue_arc);
          for expired_t in expired_tasks {
            if expired_t.retries > 1 {
              // Too many retries, mark as fatal failure
              server::push_done_queue(
                &done_queue_arc,
                TaskReport {
                  task: expired_t.task.clone(),
                  status: TaskStatus::Fatal,
                  messages: vec![NewTaskMessage::new(
                    expired_t.task.id,
                    "fatal",
                    "cortex".to_string(),
                    "never_completed_with_retries".to_string(),
                    String::new(),
                  )],
                },
              );
            } else {
              // We can still retry, re-add to the dispatch queue
              task_queue.push(TaskProgress {
                task: expired_t.task,
                created_at: expired_t.created_at,
                retries: expired_t.retries + 1,
              });
            }
          }
        }
        ventilator.send_msg(identity, SNDMORE)?;
        if let Some(current_task_progress) = task_queue.pop() {
          dispatched_task_opt = Some(current_task_progress.clone());

          let current_task = current_task_progress.task;
          let taskid = current_task.id;
          let serviceid = current_task.service_id;
          println!(
            "Replying for source job {:?} with task id {:?}",
            source_job_count, taskid,
          );
          ventilator.send_str(&taskid.to_string(), SNDMORE)?;
          if serviceid == 1 {
            // No payload needed for init
            ventilator.send(&[], 0)?;
          } else {
            // Regular services fetch the task payload and transfer it to the worker
            let file_opt = helpers::prepare_input_stream(&current_task);
            if file_opt.is_ok() {
              let mut file = file_opt?;
              let mut total_outgoing: usize = 0;
              loop {
                // Stream input data via zmq
                let mut data = vec![0; self.message_size];
                let size = file.read(&mut data)?;
                total_outgoing += size;
                data.truncate(size);

                if size < self.message_size {
                  // If exhausted, send the last frame
                  ventilator.send(&data, 0)?;
                  // And terminate
                  break;
                } else {
                  // If more to go, send the frame and indicate there's more to come
                  ventilator.send(&data, SNDMORE)?;
                }
              }
              let responded_time = time::get_time();
              let request_duration = (responded_time - request_time).num_milliseconds();
              println!(
                "Source job {}, message size: {}, took {}ms.",
                source_job_count, total_outgoing, request_duration
              );
            } else {
              println!("-- Failed to prepare input stream for taskid {:?}", taskid);
              ventilator.send(&[], 0)?;
            }
          }
        } else {
          println!("-- No jobs in queue, sent mock task reply.");
          ventilator.send_str("0", SNDMORE)?;
          ventilator.send(&[], 0)?;
        }
      } else {
        println!(
          "-- No such service in ventilator request: {:?}",
          service_name
        );
      }
      // Record that a task has been dispatched in the progress queue
      if let Some(dispatched_task) = dispatched_task_opt {
        server::push_progress_task(&progress_queue_arc, dispatched_task);
      }
      if let Some(limit_number) = job_limit {
        if source_job_count >= limit_number {
          println!(
            "Manager job limit of {:?} reached, terminating Ventilator thread...",
            limit_number
          );
          break;
        }
      }
    }
    Ok(())
  }
}
<commit_msg>now that conversion is robust, retries are generally healthy and network-related<commit_after>extern crate tempfile;
extern crate zmq;

use std::collections::HashMap;
use std::io::Read;
use std::sync::Arc;
use std::sync::Mutex;
use time;

use backend;
use dispatcher::server;
use helpers;
use helpers::{NewTaskMessage, TaskProgress, TaskReport, TaskStatus};
use models::Service;
use std::error::Error;
use zmq::SNDMORE;

/// Specifies the binding and operation parameters for a ZMQ ventilator component
pub struct Ventilator {
  /// port to listen on
  pub port: usize,
  /// the size of the dispatch queue
  /// (also the batch size for Task store queue requests)
  pub queue_size: usize,
  /// size of an individual message chunk sent via zeromq
  /// (keep this small to avoid large RAM use, increase to reduce network bandwidth)
  pub message_size: usize,
  /// address for the Task store postgres endpoint
  pub backend_address: String,
}

impl Ventilator {
  /// Starts a new dispatch `Server` (ZMQ Ventilator), to serve tasks to processing workers.
  /// The ventilator shares state with other manager threads via queues for tasks in progress,
  /// as well as a queue for completed tasks pending persisting to disk.
  /// A job limit can be provided as a termination condition for the sink server.
  pub fn start(
    &self,
    services_arc: Arc<Mutex<HashMap<String, Option<Service>>>>,
    progress_queue_arc: Arc<Mutex<HashMap<i64, TaskProgress>>>,
    done_queue_arc: Arc<Mutex<Vec<TaskReport>>>,
    job_limit: Option<usize>,
  ) -> Result<(), Box<Error>>
  {
    // We have a Ventilator-exclusive "queues" stack for tasks to be dispatched
    let mut queues: HashMap<String, Vec<TaskProgress>> = HashMap::new();
    // Assuming this is the only And tidy up the postgres tasks:
    let backend = backend::from_address(&self.backend_address);
    backend.clear_limbo_tasks()?;
    // Ok, let's bind to a port and start broadcasting
    let context = zmq::Context::new();
    let ventilator = context.socket(zmq::ROUTER)?;
    let port_str = self.port.to_string();
    let address = "tcp://*:".to_string() + &port_str;
    assert!(ventilator.bind(&address).is_ok());
    let mut source_job_count: usize = 0;

    loop {
      let mut msg = zmq::Message::new()?;
      let mut identity = zmq::Message::new()?;
      ventilator.recv(&mut identity, 0)?;
      ventilator.recv(&mut msg, 0)?;
      let service_name = msg.as_str().unwrap_or_default().to_string();
      // println!("Task requested for service: {}", service_name.clone());
      let request_time = time::get_time();
      source_job_count += 1;

      let mut dispatched_task_opt: Option<TaskProgress> = None;
      // Requests for unknown service names will be silently ignored.
      if let Some(service) = server::get_sync_service(&service_name, &services_arc, &backend) {
        if !queues.contains_key(&service_name) {
          queues.insert(service_name.clone(), Vec::new());
        }
        let mut task_queue: &mut Vec<TaskProgress> = queues
          .get_mut(&service_name)
          .unwrap_or_else(|| panic!("Could not obtain queue mutex lock in main ventilator loop"));
        if task_queue.is_empty() {
          println!(
            "-- No tasks in task queue for service {:?}, fetching up to {:?} more from backend...",
            service_name, self.queue_size
          );
          // Refetch a new batch of tasks
          let now = time::get_time().sec;
          let fetched_tasks = backend
            .fetch_tasks(&service, self.queue_size)
            .unwrap_or(Vec::new());
          task_queue.extend(fetched_tasks.into_iter().map(|task| TaskProgress {
            task,
            created_at: now,
            retries: 0,
          }));

          // This is a good time to also take care that none of the old tasks are dead in the
          // progress queue since the re-fetch happens infrequently, and directly
          // implies the progress queue will grow
          let expired_tasks = server::timeout_progress_tasks(&progress_queue_arc);
          for expired_t in expired_tasks {
            if expired_t.retries > 4 {
              // Too many retries, mark as fatal failure
              server::push_done_queue(
                &done_queue_arc,
                TaskReport {
                  task: expired_t.task.clone(),
                  status: TaskStatus::Fatal,
                  messages: vec![NewTaskMessage::new(
                    expired_t.task.id,
                    "fatal",
                    "cortex".to_string(),
                    "never_completed_with_retries".to_string(),
                    String::new(),
                  )],
                },
              );
            } else {
              // We can still retry, re-add to the dispatch queue
              task_queue.push(TaskProgress {
                task: expired_t.task,
                created_at: expired_t.created_at,
                retries: expired_t.retries + 1,
              });
            }
          }
        }
        ventilator.send_msg(identity, SNDMORE)?;
        if let Some(current_task_progress) = task_queue.pop() {
          dispatched_task_opt = Some(current_task_progress.clone());

          let current_task = current_task_progress.task;
          let taskid = current_task.id;
          let serviceid = current_task.service_id;
          println!(
            "Replying for source job {:?} with task id {:?}",
            source_job_count, taskid,
          );
          ventilator.send_str(&taskid.to_string(), SNDMORE)?;
          if serviceid == 1 {
            // No payload needed for init
            ventilator.send(&[], 0)?;
          } else {
            // Regular services fetch the task payload and transfer it to the worker
            let file_opt = helpers::prepare_input_stream(&current_task);
            if file_opt.is_ok() {
              let mut file = file_opt?;
              let mut total_outgoing: usize = 0;
              loop {
                // Stream input data via zmq
                let mut data = vec![0; self.message_size];
                let size = file.read(&mut data)?;
                total_outgoing += size;
                data.truncate(size);

                if size < self.message_size {
                  // If exhausted, send the last frame
                  ventilator.send(&data, 0)?;
                  // And terminate
                  break;
                } else {
                  // If more to go, send the frame and indicate there's more to come
                  ventilator.send(&data, SNDMORE)?;
                }
              }
              let responded_time = time::get_time();
              let request_duration = (responded_time - request_time).num_milliseconds();
              println!(
                "Source job {}, message size: {}, took {}ms.",
                source_job_count, total_outgoing, request_duration
              );
            } else {
              println!("-- Failed to prepare input stream for taskid {:?}", taskid);
              ventilator.send(&[], 0)?;
            }
          }
        } else {
          println!("-- No jobs in queue, sent mock task reply.");
          ventilator.send_str("0", SNDMORE)?;
          ventilator.send(&[], 0)?;
        }
      } else {
        println!(
          "-- No such service in ventilator request: {:?}",
          service_name
        );
      }
      // Record that a task has been dispatched in the progress queue
      if let Some(dispatched_task) = dispatched_task_opt {
        server::push_progress_task(&progress_queue_arc, dispatched_task);
      }
      if let Some(limit_number) = job_limit {
        if source_job_count >= limit_number {
          println!(
            "Manager job limit of {:?} reached, terminating Ventilator thread...",
            limit_number
          );
          break;
        }
      }
    }
    Ok(())
  }
}
<|endoftext|>


<commit_before>extern crate libc;

use indy::api::payments::indy_register_payment_method;
use std::sync::atomic::Ordering;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::ATOMIC_USIZE_INIT;
use std::ffi::CString;
use std::os::raw::c_char;

type CommonResponseCallback = extern fn(command_handle_: i32,
                                        err: i32,
                                        res1: *const c_char) -> i32;

pub extern fn init() {
    let _cmd_handle = get_next_id();
    let payment_method_name = CString::new("null_payment_plugin").unwrap();
    indy_register_payment_method(
        _cmd_handle,
        payment_method_name.as_ptr(),
        Some(create_payment_address),
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None,
        None
    );
}

extern fn create_payment_address(
    cmd_handle:i32,
    config: *const c_char,
    cb: Option<extern fn(command_handle_: i32,
                         err: i32,
                         payment_address: *const c_char) -> i32>
) -> i32 {
    let addr = CString::new("pay:null_payment_plugin:null").unwrap();
    cb.unwrap()(cmd_handle, 0, addr.as_ptr())
}

extern fn

lazy_static! {
    static ref IDS_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; //TODO use AtomicI32
}

fn get_next_id() -> i32 {
    (IDS_COUNTER.fetch_add(1, Ordering::SeqCst) + 1) as i32
}<commit_msg>Add injection of results returned in null payment plugin<commit_after>extern crate libc;

use indy::api::payments::indy_register_payment_method;
use indy::api::ledger::indy_build_get_txn_request;
use indy::api::ErrorCode;
use std::sync::Mutex;
use std::sync::atomic::Ordering;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::ATOMIC_USIZE_INIT;
use std::collections::HashMap;
use std::ffi::CString;
use std::os::raw::c_char;
use std::ffi::CStr;

type CommonResponseCallback = extern fn(command_handle_: i32,
                                        err: ErrorCode,
                                        res1: *const c_char) -> ErrorCode;

lazy_static! {
    static ref IDS_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; //TODO use AtomicI32
}

pub extern fn nullpayment_init(cb: Option<extern fn(cmd_handle: i32, err: ErrorCode)>) {
    let _cmd_handle = get_next_id();
    let payment_method_name = CString::new("null_payment_plugin").unwrap();

    indy_register_payment_method(
        _cmd_handle,
        payment_method_name.as_ptr(),
        Some(_create_payment_address_handler),
        Some(_add_request_fees_handler),
        Some(_parse_response_with_fees_handler),
        Some(_build_get_utxo_request_handler),
        Some(_parse_get_utxo_response_handler),
        Some(_build_payment_req_handler),
        Some(_parse_payment_response_handler),
        Some(_build_mint_req_handler),
        Some(_build_set_txn_fees_req_handler),
        Some(_build_get_txn_fees_req_handler),
        Some(_parse_get_txn_fees_response_handler),
        cb
    );
}

lazy_static! {
      static ref CREATE_ADDRESS_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref ADD_REQUEST_FEES_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref PARSE_RESPONSE_WITH_FEES_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_GET_UTXO_REQUEST_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref PARSE_GET_UTXO_RESPONSE_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_PAYMENT_REQ_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref PARSE_PAYMENT_RESPONSE_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_MINT_REQ_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_SET_TXN_FEES_REQ_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref BUILD_GET_TXN_FEES_REQ_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
      static ref PARSE_GET_TXN_FEES_RESPONSE_RESULT_INJECTIONS: Mutex<Vec<(ErrorCode, CString)>> = Default::default();
}

extern fn _clear_vec(vec: Mutex<Vec<(ErrorCode, CString)>>) {
    let mut vec = vec.lock().unwrap();
    vec.clear();
}

extern fn _create_payment_address_handler(
    cmd_handle:i32,
    _config: *const c_char,
    cb: Option<CommonResponseCallback>) -> ErrorCode {

    let addr = CString::new("pay:null:_payment_plugin:null").unwrap();
    let mut results = CREATE_ADDRESS_RESULT_INJECTIONS.lock().unwrap();
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, addr)
    };
    _execute_cb(cb, cmd_handle, res, err)
}

pub extern fn nullpayment_inject_create_payment_address_result(err: ErrorCode, res: *const c_char) {
    let mut results = CREATE_ADDRESS_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_create_payment_address_injections() {
    let mut vec = CREATE_ADDRESS_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _add_request_fees_handler(
    command_handle: i32,
    req_json: *const c_char,
    _inputs_json: *const c_char,
    _outputs_json: *const c_char,
    cb: Option<CommonResponseCallback>) -> ErrorCode {
    let mut results = ADD_REQUEST_FEES_RESULT_INJECTIONS.lock().unwrap();
    let req_json = CString::from(unsafe { CStr::from_ptr(req_json) });
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, req_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_add_request_fees_result(err: ErrorCode, res: *const c_char) {
    let mut results = ADD_REQUEST_FEES_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_add_request_fees_injections() {
    let mut vec = ADD_REQUEST_FEES_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _parse_response_with_fees_handler(
    command_handle: i32,
    resp_json: *const c_char,
    cb: Option<CommonResponseCallback>) -> ErrorCode {
    let mut results = PARSE_RESPONSE_WITH_FEES_RESULT_INJECTIONS.lock().unwrap();
    let resp_json = CString::from(unsafe { CStr::from_ptr(resp_json) });
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, resp_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_parse_response_with_fees_result(err: ErrorCode, res: *const c_char) {
    let mut results = PARSE_RESPONSE_WITH_FEES_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_parse_response_with_fees_injections() {
    let mut vec = PARSE_RESPONSE_WITH_FEES_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_get_utxo_request_handler(
    command_handle: i32,
    _payment_address: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_GET_UTXO_REQUEST_RESULT_INJECTIONS.lock().unwrap();
    let res = match results.is_empty() {
        false => Some(results.remove(0)),
        true => None
    };
    _build_get_txn_request(command_handle, cb, res)
}
pub extern fn nullpayment_inject_build_get_utxo_request_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_GET_UTXO_REQUEST_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_get_utxo_request_injections() {
    let mut vec = BUILD_GET_UTXO_REQUEST_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _parse_get_utxo_response_handler(
    command_handle: i32,
    _resp_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let utxo_example = r#"[{"input":"pov:null:1", "amount":1, "extra":"1"}, {"input":"pov:null:2", "amount":2, "extra":"2"}]"#;
    let utxo_json = CString::new(utxo_example).unwrap();
    let mut results = PARSE_GET_UTXO_RESPONSE_RESULT_INJECTIONS.lock().unwrap();

    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, utxo_json)
    };

    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_parse_get_utxo_response_result(err: ErrorCode, res: *const c_char) {
    let mut results = PARSE_GET_UTXO_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_parse_get_utxo_response_injections() {
    let mut vec = PARSE_GET_UTXO_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_payment_req_handler(
    command_handle: i32,
    _inputs_json: *const c_char,
    outputs_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_PAYMENT_REQ_RESULT_INJECTIONS.lock().unwrap();
    let outputs_json = CString::from(unsafe { CStr::from_ptr(outputs_json) });
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, outputs_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_build_payment_req_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_PAYMENT_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_payment_req_injections() {
    let mut vec = BUILD_PAYMENT_REQ_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _parse_payment_response_handler(
    command_handle: i32,
    resp_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = PARSE_PAYMENT_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let resp_json = CString::from(unsafe { CStr::from_ptr(resp_json) });
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, resp_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_parse_payment_response_result(err: ErrorCode, res: *const c_char){
    let mut results = PARSE_PAYMENT_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_parse_payment_response_injections() {
    let mut vec = PARSE_PAYMENT_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_mint_req_handler(
    command_handle: i32,
    _outputs_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_MINT_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = match results.is_empty() {
        false => Some(results.remove(0)),
        true => None
    };
    _build_get_txn_request(command_handle, cb, res)
}

pub extern fn nullpayment_inject_build_mint_req_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_MINT_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_mint_req_injections() {
    let mut vec = BUILD_MINT_REQ_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_set_txn_fees_req_handler(
    command_handle: i32,
    outputs_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_SET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
    let outputs_json = CString::from(unsafe { CStr::from_ptr( outputs_json )});
    let (err, res) = match results.is_empty() {
        false => results.remove(0),
        true => (ErrorCode::Success, outputs_json)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_build_set_txn_fees_req_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_SET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_set_txn_fees_req_injections() {
    let mut vec = BUILD_SET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _build_get_txn_fees_req_handler(
    command_handle: i32,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let mut results = BUILD_GET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = match results.is_empty() {
        false => Some(results.remove(0)),
        true => None
    };
    _build_get_txn_request(command_handle, cb, res)
}

pub extern fn nullpayment_inject_build_get_txn_fees_req_result(err: ErrorCode, res: *const c_char) {
    let mut results = BUILD_GET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_build_get_txn_fees_req_injections() {
    let mut vec = BUILD_GET_TXN_FEES_REQ_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

extern fn _parse_get_txn_fees_response_handler(
    command_handle: i32,
    _resp_json: *const c_char,
    cb: Option<CommonResponseCallback>
) -> ErrorCode {
    let parsed_response = CString::new(
        r#"{"txnType1":1, "txnType2":2, "txnType3":3}"#
    ).unwrap();
    let mut results = PARSE_GET_TXN_FEES_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let (err, res) = match results.is_empty() {
        true => (ErrorCode::Success, parsed_response),
        false => results.remove(0)
    };
    _execute_cb(cb, command_handle, res, err)
}

pub extern fn nullpayment_inject_parse_get_txn_fees_response_result(err: ErrorCode, res: *const c_char) {
    let mut results = PARSE_GET_TXN_FEES_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
    let res = CString::from(unsafe { CStr::from_ptr(res) });
    results.push((err, res));
}

pub extern fn nullpayment_clear_parse_get_txn_fees_response_injections() {
    let mut vec = PARSE_GET_TXN_FEES_RESPONSE_RESULT_INJECTIONS.lock().unwrap();
	vec.clear();
}

fn _execute_cb(
    cb: Option<CommonResponseCallback>,
    command_handle: i32,
    res: CString,
    err: ErrorCode
) -> ErrorCode {
    cb.unwrap_or_else(|| {
        extern fn cb (_a: i32, _b: ErrorCode, _c: *const c_char) -> ErrorCode { ErrorCode::UnknownPaymentMethod }
        cb
    })(command_handle, err, res.as_ptr())
}

fn _build_get_txn_request(
    command_handle: i32,
    cb: Option<CommonResponseCallback>,
    res: Option<(ErrorCode, CString)>
) -> ErrorCode {
    match res {
        Some((err, res)) => _execute_cb(cb, command_handle, res, err),
        None => {
            lazy_static!{
                static ref CALLBACKS : Mutex<HashMap<i32, Box<CommonResponseCallback>>> = Default::default();
            }

            extern "C" fn _callback(command_handle:i32, err: ErrorCode, req_json: *const c_char) {
                let mut map = CALLBACKS.lock().unwrap();
                let _cb = map.remove(&command_handle);
                //WARN: maybe dereferencing is needed
                _cb.unwrap()(command_handle, err, req_json);
            }

            let mut map = CALLBACKS.lock().unwrap();
            map.insert(command_handle, Box::new(cb.unwrap()));

            let submitter_did = CString::new("null_payment_plugin").unwrap();

            indy_build_get_txn_request(
                command_handle,
                submitter_did.as_ptr(),
                1,
                Some(_callback)
            );
            ErrorCode::Success
        }
    }
}

fn get_next_id() -> i32 {
    (IDS_COUNTER.fetch_add(1, Ordering::SeqCst) + 1) as i32
}<|endoftext|>


<commit_before>use std::{io, fs};

macro_rules! readln {
    () => {
        {
            let mut line = String::new();
            match io::stdin().read_line(&mut line) {
                Ok(n) => Some(line.trim().to_string()),
                Err(e) => None
            }
        }
    };
}

fn console_title(title: &str) {

}

#[no_mangle]
pub fn main() {
    console_title("Test");

    println!("Type help for a command list");
    while let Some(line) = readln!() {
        let args: Vec<String> = line.split(' ').map(|arg| arg.to_string()).collect();

        if let Some(command) = args.get(0) {
            println!("# {}", line);

            match &command[..]
            {
                "panic" => panic!("Test panic"),
                "ls" => {
                    // TODO: when libredox is completed
                    //fs::read_dir("/").unwrap().map(|dir| println!("{}", dir));
                }
                _ => println!("Commands: panic"),
            }
        }
    }
}
<commit_msg>Use an array to label all commands and add `ptr_write` command<commit_after>#![feature(alloc)]
#![feature(core)]

extern crate alloc;
extern crate core;

use alloc::boxed::Box;
use std::{io, fs, rand};
use core::ptr;

macro_rules! readln {
    () => {
        {
            let mut line = String::new();
            match io::stdin().read_line(&mut line) {
                Ok(n) => Some(line.trim().to_string()),
                Err(e) => None
            }
        }
    };
}

fn console_title(title: &str) {

}

#[no_mangle]
pub fn main() {
    console_title("Test");

    println!("Type help for a command list");
    while let Some(line) = readln!() {
        let args: Vec<String> = line.split(' ').map(|arg| arg.to_string()).collect();

        if let Some(command) = args.get(0) {
            println!("# {}", line);
            let console_commands = ["panic", "ls", "ptr_write"];

            match &command[..]
            {
                command if command == console_commands[0] => panic!("Test panic"),
                command if command == console_commands[1] => {
                    // TODO: import std::fs functions into libredox
                    //fs::read_dir("/").unwrap().map(|dir| println!("{}", dir));
                }
                command if command == console_commands[2] => {
                    let a_ptr = rand() as *mut u8;
                    // TODO: import Box::{from_raw, to_raw} methods in libredox
                    //let mut a_box = Box::new(rand() as u8);
                    unsafe {
                        ptr::write(a_ptr, rand() as u8);
                        //ptr::write(a_box.to_raw(), rand() as u8);
                    }
                }
                _ => println!("Commands: {}", console_commands.join(" ")),
            }
        }
    }
}
<|endoftext|>