<commit_before>
pub( crate ) mod private
{
  use crate::command::*;
  use crate::instruction::*;
  use wtools::meta::*;
  use wtools::error::*;
  use wtools::former::Former;

  ///
  /// Commands aggregator.
  ///

  /*
     Dmytro : owned types are used because Former does not work with combination of links and
     containers
   */

  #[ derive( Debug, PartialEq ) ]
  #[ derive( Former ) ]
  #[ allow( missing_docs ) ]
  pub struct CommandsAggregator
  {
    pub base_path : Option<std::path::PathBuf>,
    #[ default( "".to_string() ) ]
    pub command_prefix : String,
    #[ default( vec![ ".".to_string(), " ".to_string() ] ) ]
    pub delimeter : Vec< String >,
    #[ default( ";".to_string() ) ]
    pub command_explicit_delimeter : String,
    #[ default( " ".to_string() ) ]
    pub command_implicit_delimeter : String,
    #[ default( true ) ]
    pub commands_explicit_delimiting : bool,
    #[ default( false ) ]
    pub commands_implicit_delimiting : bool,
    #[ default( false ) ]
    pub properties_map_parsing : bool,
    #[ default( true ) ]
    pub several_values : bool,
    #[ default( true ) ]
    pub with_help : bool,
    #[ default( true ) ]
    pub changing_exit_code : bool,
    // logger : Option<Logger>, /* qqq : implement */
    pub commands : std::collections::HashMap< String, Command >,
    // pub vocabulary : Option<vocabulary>, /* qqq : implement */
  }

  impl CommandsAggregator
  {
    /// Perform instructions queue as single program.
    pub fn program_perform( &self, _program : &str ) -> Result< (), BasicError >
    {
      unimplemented!( "not implemented" );
      // let parsed = program_parse( program );
    }

    /// Perform instruction.
    pub fn instruction_perform( &self, instruction : impl AsRef< str > ) -> Result< (), BasicError >
    {
      let parsed : Instruction = instruction_parse()
      .instruction( instruction.as_ref() )
      .several_values( self.several_values )
      .properties_map_parsing( self.properties_map_parsing )
      .quoting( true )
      .unquoting( true )
      .perform();

      let result = match self.command_resolve( &parsed )
      {
        Some( command ) =>
        {
          command.perform( &parsed )
        },
        None =>
        {
          if self.with_help
          {
            self.on_get_help().unwrap();
          }
          if self.changing_exit_code
          {
            std::process::exit( 1 );
          }
          Ok( () )
        },
      };

      result
    }

    /// Print help for command.
    fn command_help( &self, command : impl AsRef< str > )
    {
      /* qqq : command should print help, not CA */
      if command.as_ref() == ""
      {
        for ( command_name, command_descriptor ) in self.commands.iter()
        {
          println!( "{} - {}", command_name, command_descriptor.hint );
        }
      }
      else
      {
        if let Some( command_descriptor ) = self.commands.get( command.as_ref() )
        {
          println!( "{} - {}", command.as_ref(), command_descriptor.hint );
        }
      }
    }

    /// Find command in dictionary.
    fn command_resolve( &self, instruction : &Instruction ) -> Option<&Command>
    {
      self.commands.get( &instruction.command_name )
    }
  }

  //

  ///
  /// On error helper.
  ///

  pub trait OnError
  {
    /// Handle error.
    fn on_error( &self, err : BasicError ) -> Result< (), BasicError >;
  }

  ///
  /// On syntax error helper.
  ///

  pub trait OnSyntaxError
  {
    /// Handle syntax error.
    fn on_syntax_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >;
  }

  ///
  /// On ambiguity helper.
  ///

  pub trait OnAmbiguity
  {
    /// Handle ambiguity.
    fn on_ambiguity( &self, command : impl AsRef< str > ) -> Result< (), BasicError >;
  }

  ///
  /// On unknown command error helper.
  ///

  pub trait OnUnknownCommandError
  {
    /// Handle unknown command error.
    fn on_unknown_command_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >;
  }

  ///
  /// Help helper.
  ///

  pub trait OnGetHelp
  {
    /// Get help.
    fn on_get_help( &self ) -> Result< (), BasicError >;
  }

  ///
  /// Printing commands helper.
  ///

  pub trait OnPrintCommands
  {
    /// Print all commands.
    fn on_print_commands( &self ) -> Result< (), BasicError >;
  }

  ///
  /// Super trait that checks that all helpers are implemented.
  ///

  pub trait CommandsAggregatorHandlers : OnError + OnSyntaxError + OnAmbiguity + OnUnknownCommandError + OnGetHelp + OnPrintCommands
  {
  }

  impl CommandsAggregatorHandlers for CommandsAggregator {}

  #[ cfg( feature = "on_error_default" ) ]
  impl OnError for CommandsAggregator
  {
    /// Handle error.
    fn on_error( &self, err : BasicError ) -> Result< (), BasicError >
    {
      if self.changing_exit_code
      {
        unimplemented!();
      }
      Err( err )
    }
  }

  #[ cfg( feature = "on_syntax_error_default" ) ]
  impl OnSyntaxError for CommandsAggregator
  {
    /// Handle syntax error.
    fn on_syntax_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >
    {
      let err_formatted = format!( "Illformed command \"{}\"", command.as_ref() );
      eprintln!( "{}", err_formatted );
      self.on_get_help().unwrap();

      let err = BasicError::new( err_formatted );
      return self.on_error( err );
    }
  }

  #[ cfg( feature = "on_ambiguity_default" ) ]
  impl OnAmbiguity for CommandsAggregator
  {
    /// Handle ambiguity.
    fn on_ambiguity( &self, command : impl AsRef< str > ) -> Result< (), BasicError >
    {
      eprintln!( "Ambiguity. Did you mean?" );
      self.command_help( command.as_ref() );
      println!( "" );

      let err_formatted = format!( "Ambiguity \"{}\"", command.as_ref() );
      let err = BasicError::new( err_formatted );
      return self.on_error( err );
    }
  }

  #[ cfg( feature = "on_unknown_command_error_default" ) ]
  impl OnUnknownCommandError for CommandsAggregator
  {
    /// Handle unknown command error.
    fn on_unknown_command_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >
    {
      let mut err_formatted = format!( "Unknown command \"{}\"", command.as_ref() );

      let instruction = instruction_parse()
      .instruction( ".help" )
      .perform();
      if self.command_resolve( &instruction ).is_some()
      {
        err_formatted.push_str( "\nTry \".help\"" );
      }
      let err = BasicError::new( err_formatted );
      return self.on_error( err );
    }
  }

  #[ cfg( feature = "on_get_help_default" ) ]
  impl OnGetHelp for CommandsAggregator
  {
    /// Get help.
    fn on_get_help( &self ) -> Result< (), BasicError >
    {
      let instruction = instruction_parse()
      .instruction( ".help" )
      .perform();
      if let Some( command ) = self.command_resolve( &instruction )
      {
        let instruction = instruction_parse()
        .instruction( "" )
        .perform();
        return command.perform( &instruction );
      }
      else
      {
        self.command_help( "" );
        return Ok( () );
      }
    }
  }

  #[ cfg( feature = "on_print_commands_default" ) ]
  impl OnPrintCommands for CommandsAggregator
  {
    /// Print all commands.
    fn on_print_commands( &self ) -> Result< (), BasicError >
    {
      println!( "" );
      self.command_help( "" );
      println!( "" );
      Ok( () )
    }
  }

  //

  ///
  /// Get instruction parser builder.
  ///

  pub fn commands_aggregator() -> CommandsAggregatorFormer
  {
    CommandsAggregator::former()
  }

}

/// Protected namespace of the module.
pub mod protected
{
  pub use super::private::CommandsAggregator;
  pub use super::private::commands_aggregator;
}

pub use protected::*;

/// Exposed namespace of the module.
pub mod exposed
{
  pub use super::prelude::*;
}

/// Namespace of the module to include with `use module::*`.
pub mod prelude
{
  pub use super::private::CommandsAggregator;
  pub use super::private::commands_aggregator;
}

<commit_msg>Implement routine `instructions_parse`<commit_after>
pub( crate ) mod private
{
  use crate::command::*;
  use crate::instruction::*;
  use wtools::meta::*;
  use wtools::error::*;
  use wtools::string::split;
  use wtools::former::Former;

  ///
  /// Commands aggregator.
  ///

  /*
     Dmytro : owned types are used because Former does not work with combination of links and
     containers
   */

  #[ derive( Debug, PartialEq ) ]
  #[ derive( Former ) ]
  #[ allow( missing_docs ) ]
  pub struct CommandsAggregator
  {
    pub base_path : Option<std::path::PathBuf>,
    #[ default( "".to_string() ) ]
    pub command_prefix : String,
    #[ default( vec![ ".".to_string(), " ".to_string() ] ) ]
    pub delimeter : Vec< String >,
    #[ default( ";".to_string() ) ]
    pub command_explicit_delimeter : String,
    #[ default( " ".to_string() ) ]
    pub command_implicit_delimeter : String,
    #[ default( true ) ]
    pub commands_explicit_delimiting : bool,
    #[ default( false ) ]
    pub commands_implicit_delimiting : bool,
    #[ default( false ) ]
    pub properties_map_parsing : bool,
    #[ default( true ) ]
    pub several_values : bool,
    #[ default( true ) ]
    pub with_help : bool,
    #[ default( true ) ]
    pub changing_exit_code : bool,
    // logger : Option<Logger>, /* qqq : implement */
    pub commands : std::collections::HashMap< String, Command >,
    // pub vocabulary : Option<vocabulary>, /* qqq : implement */
  }

  impl CommandsAggregator
  {
    /// Perform instructions queue as single program.
    pub fn program_perform( &self, _program : &str ) -> Result< (), BasicError >
    {
      unimplemented!( "not implemented" );
      // let parsed = program_parse( program );
    }

    /// Perform instruction.
    pub fn instruction_perform( &self, instruction : impl AsRef< str > ) -> Result< (), BasicError >
    {
      let parsed : Instruction = instruction_parse()
      .instruction( instruction.as_ref() )
      .several_values( self.several_values )
      .properties_map_parsing( self.properties_map_parsing )
      .quoting( true )
      .unquoting( true )
      .perform();

      let result = match self.command_resolve( &parsed )
      {
        Some( command ) =>
        {
          command.perform( &parsed )
        },
        None =>
        {
          if self.with_help
          {
            self.on_get_help().unwrap();
          }
          if self.changing_exit_code
          {
            std::process::exit( 1 );
          }
          Ok( () )
        },
      };

      result
    }

    /// Print help for command.
    fn command_help( &self, command : impl AsRef< str > )
    {
      /* qqq : command should print help, not CA */
      if command.as_ref() == ""
      {
        for ( command_name, command_descriptor ) in self.commands.iter()
        {
          println!( "{} - {}", command_name, command_descriptor.hint );
        }
      }
      else
      {
        if let Some( command_descriptor ) = self.commands.get( command.as_ref() )
        {
          println!( "{} - {}", command.as_ref(), command_descriptor.hint );
        }
      }
    }

    /// Find command in dictionary.
    fn command_resolve( &self, instruction : &Instruction ) -> Option<&Command>
    {
      self.commands.get( &instruction.command_name )
    }

    /// Parse multiple instructions.
    pub fn instructions_parse( &self, program : impl AsRef< str > ) -> Vec< Instruction >
    {
      let commands = split()
      .src( program.as_ref().trim() )
      .delimeter( self.command_explicit_delimeter.as_str() )
      .preserving_empty( false )
      .preserving_delimeters( false )
      .preserving_quoting( false )
      .perform();
      let commands = commands.map( | e | String::from( e ) ).collect::< Vec< _ > >();

      let mut string_commands = vec![];
      for command in commands
      {
        let splitted = split()
        .src( command.trim() )
        .delimeter( self.command_implicit_delimeter.as_str() )
        .preserving_empty( false )
        .preserving_delimeters( false )
        .preserving_quoting( false )
        .perform();
        let splitted = splitted.map( | e | String::from( e ) ).collect::< Vec< _ > >();

        if self.command_implicit_delimeter == " "
        {
          let start_index = if splitted[ 0 ].is_empty() { 1 } else { 0 };
          let mut string_command = String::from( &splitted[ start_index ] );

          for i in start_index + 1 .. splitted.len()
          {
            let part = splitted[ i ].trim();
            if part.starts_with( '.' )
            {
              string_commands.push( string_command );
              string_command = String::from( part );
            }
            else
            {
              string_command.push( ' ' );
              string_command.push_str( part );
            }
          }

          string_commands.push( string_command );
        }
        else
        {
          for command in splitted
          {
            string_commands.push( String::from( command.trim() ) );
          }
        }
      }

      let instructions = string_commands.iter().map( | instruction |
      {
        instruction_parse()
        .instruction( instruction.as_str() )
        .properties_map_parsing( true )
        .several_values( true )
        .perform()
      }).collect::< Vec< Instruction > >();

      instructions
    }
  }

  //

  ///
  /// On error helper.
  ///

  pub trait OnError
  {
    /// Handle error.
    fn on_error( &self, err : BasicError ) -> Result< (), BasicError >;
  }

  ///
  /// On syntax error helper.
  ///

  pub trait OnSyntaxError
  {
    /// Handle syntax error.
    fn on_syntax_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >;
  }

  ///
  /// On ambiguity helper.
  ///

  pub trait OnAmbiguity
  {
    /// Handle ambiguity.
    fn on_ambiguity( &self, command : impl AsRef< str > ) -> Result< (), BasicError >;
  }

  ///
  /// On unknown command error helper.
  ///

  pub trait OnUnknownCommandError
  {
    /// Handle unknown command error.
    fn on_unknown_command_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >;
  }

  ///
  /// Help helper.
  ///

  pub trait OnGetHelp
  {
    /// Get help.
    fn on_get_help( &self ) -> Result< (), BasicError >;
  }

  ///
  /// Printing commands helper.
  ///

  pub trait OnPrintCommands
  {
    /// Print all commands.
    fn on_print_commands( &self ) -> Result< (), BasicError >;
  }

  ///
  /// Super trait that checks that all helpers are implemented.
  ///

  pub trait CommandsAggregatorHandlers : OnError + OnSyntaxError + OnAmbiguity + OnUnknownCommandError + OnGetHelp + OnPrintCommands
  {
  }

  impl CommandsAggregatorHandlers for CommandsAggregator {}

  #[ cfg( feature = "on_error_default" ) ]
  impl OnError for CommandsAggregator
  {
    /// Handle error.
    fn on_error( &self, err : BasicError ) -> Result< (), BasicError >
    {
      if self.changing_exit_code
      {
        unimplemented!();
      }
      Err( err )
    }
  }

  #[ cfg( feature = "on_syntax_error_default" ) ]
  impl OnSyntaxError for CommandsAggregator
  {
    /// Handle syntax error.
    fn on_syntax_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >
    {
      let err_formatted = format!( "Illformed command \"{}\"", command.as_ref() );
      eprintln!( "{}", err_formatted );
      self.on_get_help().unwrap();

      let err = BasicError::new( err_formatted );
      return self.on_error( err );
    }
  }

  #[ cfg( feature = "on_ambiguity_default" ) ]
  impl OnAmbiguity for CommandsAggregator
  {
    /// Handle ambiguity.
    fn on_ambiguity( &self, command : impl AsRef< str > ) -> Result< (), BasicError >
    {
      eprintln!( "Ambiguity. Did you mean?" );
      self.command_help( command.as_ref() );
      println!( "" );

      let err_formatted = format!( "Ambiguity \"{}\"", command.as_ref() );
      let err = BasicError::new( err_formatted );
      return self.on_error( err );
    }
  }

  #[ cfg( feature = "on_unknown_command_error_default" ) ]
  impl OnUnknownCommandError for CommandsAggregator
  {
    /// Handle unknown command error.
    fn on_unknown_command_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >
    {
      let mut err_formatted = format!( "Unknown command \"{}\"", command.as_ref() );

      let instruction = instruction_parse()
      .instruction( ".help" )
      .perform();
      if self.command_resolve( &instruction ).is_some()
      {
        err_formatted.push_str( "\nTry \".help\"" );
      }
      let err = BasicError::new( err_formatted );
      return self.on_error( err );
    }
  }

  #[ cfg( feature = "on_get_help_default" ) ]
  impl OnGetHelp for CommandsAggregator
  {
    /// Get help.
    fn on_get_help( &self ) -> Result< (), BasicError >
    {
      let instruction = instruction_parse()
      .instruction( ".help" )
      .perform();
      if let Some( command ) = self.command_resolve( &instruction )
      {
        let instruction = instruction_parse()
        .instruction( "" )
        .perform();
        return command.perform( &instruction );
      }
      else
      {
        self.command_help( "" );
        return Ok( () );
      }
    }
  }

  #[ cfg( feature = "on_print_commands_default" ) ]
  impl OnPrintCommands for CommandsAggregator
  {
    /// Print all commands.
    fn on_print_commands( &self ) -> Result< (), BasicError >
    {
      println!( "" );
      self.command_help( "" );
      println!( "" );
      Ok( () )
    }
  }

  //

  ///
  /// Get instruction parser builder.
  ///

  pub fn commands_aggregator() -> CommandsAggregatorFormer
  {
    CommandsAggregator::former()
  }

}

/// Protected namespace of the module.
pub mod protected
{
  pub use super::private::CommandsAggregator;
  pub use super::private::OnError;
  pub use super::private::OnSyntaxError;
  pub use super::private::OnAmbiguity;
  pub use super::private::OnUnknownCommandError;
  pub use super::private::OnGetHelp;
  pub use super::private::OnPrintCommands;
  pub use super::private::commands_aggregator;
}

pub use protected::*;

/// Exposed namespace of the module.
pub mod exposed
{
  pub use super::prelude::*;
}

/// Namespace of the module to include with `use module::*`.
pub mod prelude
{
  pub use super::private::CommandsAggregator;
  pub use super::private::OnError;
  pub use super::private::OnSyntaxError;
  pub use super::private::OnAmbiguity;
  pub use super::private::OnUnknownCommandError;
  pub use super::private::OnGetHelp;
  pub use super::private::OnPrintCommands;
  pub use super::private::commands_aggregator;
}

<|endoftext|>


<commit_before>extern crate tinyrsh;

use std::collections::HashMap;
use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
use std::os::unix::io::{RawFd, AsRawFd, IntoRawFd};
use tinyrsh::select as select;
use tinyrsh::select::FdSet;
use std::process::{Command, Stdio};

fn greet_client(mut stream: &TcpStream) {
    let remote = stream.peer_addr().unwrap();
    println!("connected: {}", remote);

    match stream.write(b"hello") {
        Err(e) => println!("{}", e),
        Ok(n) => println!("wrote {} bytes", n),
    };
}

//what the develry! mut stream as parameter??
fn read_client(mut stream: &TcpStream) -> bool {
    let remote = stream.peer_addr().unwrap();

    let mut buffer = [0; 10];
    let cont = match stream.read(&mut buffer) {
        Err(e) => {
                println!("{}", e);
                false
            },
        Ok(n) => {
                println!("{}: {:?} ({} bytes)", remote, buffer, n);
                n != 0
            },
    };
    cont
}

fn debug_fdset(fdset: &FdSet) {
    let maxfds = select::FD_SETSIZE;
    for i in 0 .. maxfds {
        if fdset.contains(i) {
            println!("contains {}", i);
        }
    }
}

fn read_child<T: Read>(readt: &mut T) {
    let mut buf = [0; 10];
    let n = readt.read(&mut buf).expect("read_child");
    println!("{:?} ({} bytes) `{}'", buf, n, String::from_utf8_lossy(&buf[..n]));
}

fn copy_to<T: Read, U: Write>(from: &mut T, to: &mut U) -> bool {
    let mut buf = [0; 10];
    let n = from.read(&mut buf).expect("copy_to read");
    println!("copy_to read {:?} ({} bytes) `{}'", buf, n, String::from_utf8_lossy(&buf[..n]));
    let written = to.write(&buf[..n]).expect("copy_to write");
    to.flush();
    assert_eq!(n, written);

    n != 0
}

fn main() {
    println!("Hello, world!");

    let child = Command::new("../py-ptysh/ptysh.py")
            //.arg("-a")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .expect("failed to execute process");
    println!("spawned child with pid {}", child.id());

    let mut child_stdin  = child.stdin.unwrap();
    let mut child_stdout = child.stdout.unwrap();
    let mut child_stderr = child.stderr.unwrap();

        // test read output!!! not linebuffered??
        let mut buffer = [0; 10];
        let n = child_stdout.read(&mut buffer).expect("child stdout read");
        println!("childstdout: {:?} ({} bytes)", buffer, n);

    let listener = TcpListener::bind("127.0.0.1:6699").unwrap();

    let mut all_fdset = FdSet::new();
    let srv_fd = listener.as_raw_fd(); // not consumed

    //high fd for select() call
    let mut high_fd = 0;

    for fd in [srv_fd, child_stdout.as_raw_fd(), child_stderr.as_raw_fd()].iter() {
        println!("inserting fd {}", fd);
        all_fdset.insert(*fd);
        high_fd = std::cmp::max(high_fd, *fd);
    };

    let mut clients: HashMap<RawFd, TcpStream> = HashMap::new(); //could also use FromRawFd

    fn add_client(all_fdset: &mut FdSet, clients: &mut HashMap<RawFd, TcpStream>, stream: TcpStream) -> RawFd {
        let stream_fd: RawFd = stream.as_raw_fd(); //not consume
        println!("inserted fd {}", stream_fd);
        clients.insert(stream_fd, stream);
        all_fdset.insert(stream_fd);
        stream_fd
    };
    fn del_client(all_fdset: &mut FdSet, clients: &mut HashMap<RawFd, TcpStream>, stream_fd: RawFd) {
        assert!(clients.contains_key(&stream_fd));
        //let stream = clients.get(&stream_fd);
        println!("removing fd {}", stream_fd);
        assert!(all_fdset.contains(stream_fd));
        all_fdset.remove(stream_fd);
        assert!(clients.contains_key(&stream_fd));
        clients.remove(&stream_fd);
    };

    for stream in listener.incoming() {
        match stream {
            Ok(mut stream) => {
                greet_client(&mut stream);
                high_fd = std::cmp::max(high_fd, add_client(&mut all_fdset, &mut clients, stream));
                println!("new high fd: {}", high_fd);
            }
            Err(e) => { /* connection failed */ }
        }
        println!("selecting");
        loop {
            let mut fdset = all_fdset.clone();
            let res = select::select(high_fd + 1, Some(&mut fdset), None, None, None);
            assert!(res > 0);
            println!("selected returned {}", res);
            debug_fdset(&fdset);

            let mut accept_new = false;

            let mut handled_fds = 0;
            for i in 0 .. high_fd + 1 {
                if fdset.contains(i) {
                    if i == srv_fd {
                        println!("need to accept new connection");
                        accept_new = true;
                    } else if i == child_stdout.as_raw_fd() {
                        println!("child fd {} (stdout) got active", i);
                        read_child(&mut child_stdout);
                    } else if i == child_stderr.as_raw_fd() {
                        println!("child fd {} (stderr) got active", i);
                        read_child(&mut child_stderr);
                    } else {
                        assert!(clients.contains_key(&i));
                        let cont = {
                            //let ref stream = clients[&i];
                            let stream: &mut TcpStream= clients.get_mut(&i).unwrap();
                            //read_client(stream)
                            copy_to(stream, &mut child_stdin)
                        };
                        if !cont {
                            del_client(&mut all_fdset, &mut clients, i);
                        };
                    }
                    handled_fds += 1;
                }
            }
            assert_eq!(handled_fds, res);
            if accept_new {break}
        }
    }
}
<commit_msg>working rsh, panics on many things<commit_after>extern crate tinyrsh;

use std::collections::HashMap;
use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
use std::os::unix::io::{RawFd, AsRawFd, IntoRawFd};
use tinyrsh::select as select;
use tinyrsh::select::FdSet;
use std::process::{Command, Stdio};

fn greet_client(mut stream: &TcpStream) {
    let remote = stream.peer_addr().unwrap();
    println!("connected: {}", remote);

    match stream.write(b"hello") {
        Err(e) => println!("{}", e),
        Ok(n) => println!("wrote {} bytes", n),
    };
}

//what the develry! mut stream as parameter??
fn read_client(mut stream: &TcpStream) -> bool {
    let remote = stream.peer_addr().unwrap();

    let mut buffer = [0; 10];
    let cont = match stream.read(&mut buffer) {
        Err(e) => {
                println!("{}", e);
                false
            },
        Ok(n) => {
                println!("{}: {:?} ({} bytes)", remote, buffer, n);
                n != 0
            },
    };
    cont
}

fn debug_fdset(fdset: &FdSet) {
    let maxfds = select::FD_SETSIZE;
    for i in 0 .. maxfds {
        if fdset.contains(i) {
            println!("contains {}", i);
        }
    }
}

fn read_child<T: Read>(readt: &mut T) {
    let mut buf = [0; 10];
    let n = readt.read(&mut buf).expect("read_child");
    println!("{:?} ({} bytes) `{}'", buf, n, String::from_utf8_lossy(&buf[..n]));
}

fn copy_to<T: Read, U: Write>(from: &mut T, to: &mut U) -> bool {
    let mut buf = [0; 10];
    let n = from.read(&mut buf).expect("copy_to read");
    println!("copy_to read {:?} ({} bytes) `{}'", buf, n, String::from_utf8_lossy(&buf[..n]));
    let written = to.write(&buf[..n]).expect("copy_to write");
    to.flush();
    assert_eq!(n, written);

    n != 0
}

fn main() {
    println!("Hello, world!");

    let child = Command::new("../py-ptysh/ptysh.py")
            //.arg("-a")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .expect("failed to execute process");
    println!("spawned child with pid {}", child.id());

    let mut child_stdin  = child.stdin.unwrap();
    let mut child_stdout = child.stdout.unwrap();
    let mut child_stderr = child.stderr.unwrap();

        // test read output!!! not linebuffered??
        let mut buffer = [0; 10];
        let n = child_stdout.read(&mut buffer).expect("child stdout read");
        println!("childstdout: {:?} ({} bytes)", buffer, n);

    let listener = TcpListener::bind("127.0.0.1:6699").unwrap();

    let mut all_fdset = FdSet::new();
    let srv_fd = listener.as_raw_fd(); // not consumed

    //high fd for select() call
    let mut high_fd = 0;

    for fd in [srv_fd, child_stdout.as_raw_fd(), child_stderr.as_raw_fd()].iter() {
        println!("inserting fd {}", fd);
        all_fdset.insert(*fd);
        high_fd = std::cmp::max(high_fd, *fd);
    };

    let mut clients: HashMap<RawFd, TcpStream> = HashMap::new(); //could also use FromRawFd

    fn add_client(all_fdset: &mut FdSet, clients: &mut HashMap<RawFd, TcpStream>, stream: TcpStream) -> RawFd {
        let stream_fd: RawFd = stream.as_raw_fd(); //not consume
        println!("inserted fd {}", stream_fd);
        clients.insert(stream_fd, stream);
        all_fdset.insert(stream_fd);
        stream_fd
    };
    fn del_client(all_fdset: &mut FdSet, clients: &mut HashMap<RawFd, TcpStream>, stream_fd: RawFd) {
        assert!(clients.contains_key(&stream_fd));
        //let stream = clients.get(&stream_fd);
        println!("removing fd {}", stream_fd);
        assert!(all_fdset.contains(stream_fd));
        all_fdset.remove(stream_fd);
        assert!(clients.contains_key(&stream_fd));
        clients.remove(&stream_fd);
    };

    for stream in listener.incoming() {
        match stream {
            Ok(mut stream) => {
                greet_client(&mut stream);
                high_fd = std::cmp::max(high_fd, add_client(&mut all_fdset, &mut clients, stream));
                println!("new high fd: {}", high_fd);
            }
            Err(e) => { println!("connection failed!"); }
        }
        let stream = (); //todo remove outer for stream loop

        println!("selecting");
        loop {
            let mut fdset = all_fdset.clone();
            let res = select::select(high_fd + 1, Some(&mut fdset), None, None, None);
            assert!(res > 0);
            println!("selected returned {}", res);
            debug_fdset(&fdset);

            let mut accept_new = false;

            let mut handled_fds = 0;
            for i in 0 .. high_fd + 1 {
                if fdset.contains(i) {
                    if i == srv_fd {
                        println!("need to accept new connection");
                        accept_new = true;
                    } else if i == child_stdout.as_raw_fd() {
                        println!("child fd {} (stdout) got active", i);
                        //read_child(&mut child_stdout);
                        let mut buf = [0; 10];
                        let n = child_stdout.read(&mut buf).expect("child stdout read");
                        println!("read child {:?} ({} bytes) `{}'", buf, n, String::from_utf8_lossy(&buf[..n]));
                        assert!(n != 0); //child not EOF
                        for mut remote in clients.values() {
                            println!("forwarding to {}", remote.peer_addr().unwrap());
                            let written = remote.write(&buf[..n]).expect("remote write");
                            remote.flush();
                            assert_eq!(n, written);
                        }
                    } else if i == child_stderr.as_raw_fd() {
                        println!("child fd {} (stderr) got active", i);
                        read_child(&mut child_stderr);
                    } else {
                        assert!(clients.contains_key(&i));
                        let cont = {
                            let stream: &mut TcpStream = clients.get_mut(&i).unwrap();
                            copy_to(stream, &mut child_stdin)
                        };
                        if !cont {
                            del_client(&mut all_fdset, &mut clients, i);
                        };
                    }
                    handled_fds += 1;
                }
            }
            assert_eq!(handled_fds, res);
            if accept_new {break}
        }
    }
}
<|endoftext|>


<commit_before>extern crate sorting;

use std::fmt::Debug;

/// Verify order of input vector as sorted by indices.
///
/// # Details
///
/// This function takes as arguments a vector of data and a vector representing the indices of the
/// first vector making it sorted.
///
/// The elements of the sorted vector are compared to the next ones to make sure they are less
/// or equal.
///
/// # Panics
///
/// The sorting of the data vector is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// let data: Vec<i32> = vec![4, 2, 3, 1];
/// let sorted_indices: Vec<usize> = vec![3, 1, 2, 0];
/// verify_sorting(&data, &sorted_indices);
/// ```
///
fn verify_sorting<T: PartialOrd>(array: &Vec<T>, sorted_indices: &Vec<usize>) {
    let n = array.len();
    assert_eq!(n , sorted_indices.len());

    if !array.is_empty() {
        for i in 0..n-1 {
            assert!(array[sorted_indices[i]] <= array[sorted_indices[i+1]]);
        }
    }
}

/// Call the specified sorting function on an empty vector of specific type.
///
/// # Details
///
/// This function takes as arguments a pointer to a sorting function acting and call
/// it on an empty vector of type T. The call should succeed and the index vector should
/// be empty as well.
///
/// # Panics
///
/// The emptiness of the sorted list is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
/// ```
///
fn test_empty_vec<T: PartialOrd>(sorting_fct: fn(&Vec<T>) -> Vec<usize>) {
    let to_sort: Vec<T> = vec![];
    let sorted_indices = sorting_fct(&to_sort);
    assert_eq!(sorted_indices, vec![]);
    assert!(sorted_indices.is_empty());
}

/// Validate against empty vector (i8).
#[test]
fn simple_insertion_empty_vec_i8() {
    test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i16() {
    test_empty_vec::<i16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i32() {
    test_empty_vec::<i32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i64).
#[test]
fn simple_insertion_empty_vec_i64() {
    test_empty_vec::<i64>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (u8).
#[test]
fn simple_insertion_empty_vec_u8() {
    test_empty_vec::<u8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u16() {
    test_empty_vec::<u16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u32() {
    test_empty_vec::<u32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u64).
#[test]
fn simple_insertion_empty_vec_u64() {
    test_empty_vec::<u64>(sorting::simplesorts::insertion::sort);
}

/// Validate a vector of double precision values.
#[test]
fn simple_insertion_vec_f64() {
    let to_sort: Vec<f64> = vec![6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
                                 3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];

    let sorted_indices = sorting::simplesorts::insertion::sort(&to_sort);

    verify_sorting(&to_sort, &sorted_indices);

    assert_eq!(sorted_indices, vec![3, 15, 4, 2, 13, 8, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12]);
}
<commit_msg>Get rid of debug<commit_after>extern crate sorting;

/// Verify order of input vector as sorted by indices.
///
/// # Details
///
/// This function takes as arguments a vector of data and a vector representing the indices of the
/// first vector making it sorted.
///
/// The elements of the sorted vector are compared to the next ones to make sure they are less
/// or equal.
///
/// # Panics
///
/// The sorting of the data vector is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// let data: Vec<i32> = vec![4, 2, 3, 1];
/// let sorted_indices: Vec<usize> = vec![3, 1, 2, 0];
/// verify_sorting(&data, &sorted_indices);
/// ```
///
fn verify_sorting<T: PartialOrd>(array: &Vec<T>, sorted_indices: &Vec<usize>) {
    let n = array.len();
    assert_eq!(n , sorted_indices.len());

    if !array.is_empty() {
        for i in 0..n-1 {
            assert!(array[sorted_indices[i]] <= array[sorted_indices[i+1]]);
        }
    }
}

/// Call the specified sorting function on an empty vector of specific type.
///
/// # Details
///
/// This function takes as arguments a pointer to a sorting function acting and call
/// it on an empty vector of type T. The call should succeed and the index vector should
/// be empty as well.
///
/// # Panics
///
/// The emptiness of the sorted list is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
/// ```
///
fn test_empty_vec<T: PartialOrd>(sorting_fct: fn(&Vec<T>) -> Vec<usize>) {
    let to_sort: Vec<T> = vec![];
    let sorted_indices = sorting_fct(&to_sort);
    assert_eq!(sorted_indices, vec![]);
    assert!(sorted_indices.is_empty());
}

/// Validate against empty vector (i8).
#[test]
fn simple_insertion_empty_vec_i8() {
    test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i16() {
    test_empty_vec::<i16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i32() {
    test_empty_vec::<i32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i64).
#[test]
fn simple_insertion_empty_vec_i64() {
    test_empty_vec::<i64>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (u8).
#[test]
fn simple_insertion_empty_vec_u8() {
    test_empty_vec::<u8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u16() {
    test_empty_vec::<u16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u32() {
    test_empty_vec::<u32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u64).
#[test]
fn simple_insertion_empty_vec_u64() {
    test_empty_vec::<u64>(sorting::simplesorts::insertion::sort);
}

/// Validate a vector of double precision values.
#[test]
fn simple_insertion_vec_f64() {
    let to_sort: Vec<f64> = vec![6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
                                 3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];

    let sorted_indices = sorting::simplesorts::insertion::sort(&to_sort);

    verify_sorting(&to_sort, &sorted_indices);

    assert_eq!(sorted_indices, vec![3, 15, 4, 2, 13, 8, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12]);
}
<|endoftext|>


<commit_before>extern crate hydromath;

#[test]
fn test_rmse() {
    let test_obs = &[13.,17.,18.,20.,24.];
    let test_sim = &[12.,15.,20.,22.,24.];

    let result = hydromath::rmse(test_obs, test_sim);
    assert_eq!(result, (2.6 as f64).sqrt());
}

#[test]
fn test_rmse_perfect() {
    let test_obs = &[1.,2.,3.,4.,5.];
    let test_sim = &[1.,2.,3.,4.,5.];

    let result = hydromath::rmse(test_obs, test_sim);
    assert_eq!(result, (0 as f64));
}

#[test]
fn test_rmse_bad() {
    let obs: [f64; 5] = [1.,2.,3.,4.,5.];

    let sum: f64 = obs.iter().fold(0f64, std::ops::Add::add);
    let m = sum / (obs.len() as f64);
    let sim = [m, m, m, m, m];
    let result = hydromath::rmse(
        &[1.,2.,3.,4.,5.],
        &sim
    );

    assert_eq!(result, (2.0 as f64).sqrt());
}

#[test]
fn test_mse() {
    let test_obs = &[13.,17.,18.,20.,24.];
    let test_sim = &[12.,15.,20.,22.,24.];

    let result = hydromath::mse(test_obs, test_sim);
    assert_eq!(result, 2.6);
}

#[test]
fn test_mse_perfect() {
    let result = hydromath::mse(&[1.,2.,3.,4.,5.],
                  &[1.,2.,3.,4.,5.]);

    assert_eq!(result, 0.0);
}

#[test]
fn test_mse_bad() {
    let obs: [f64; 5] = [1.,2.,3.,4.,5.];

    let sum: f64 = obs.iter().fold(0f64, std::ops::Add::add);
    let m = sum / (obs.len() as f64);
    let sim = [m, m, m, m, m];
    let result = hydromath::mse(
        &[1.,2.,3.,4.,5.],
        &sim
    );

    assert_eq!(result, 2.0);
}

#[test]
fn test_nse() {
    let test_obs = &[5.,4.,6.,1.,3.,6.,8.,1.,7.,3.,4.,0.5];
    let test_sim = &[3.,4.5,4.,2.,4.,5.,9.,2.,8.,3.,4.,0.8];

    let result = hydromath::nse(test_obs, test_sim);
    assert_eq!(result, 0.783479081472161);
}

#[test]
fn test_nse_perfect() {
    let result = hydromath::nse(&[1.,2.,3.,4.,5.],
                  &[1.,2.,3.,4.,5.]);

    assert_eq!(result, 1.0);
}

#[test]
fn test_nse_bad() {
    let obs: [f64; 5] = [1.,2.,3.,4.,5.];

    let sum: f64 = obs.iter().fold(0f64, std::ops::Add::add);
    let m = sum / (obs.len() as f64);
    let sim = [m, m, m, m, m];
    let result = hydromath::nse(
        &[1.,2.,3.,4.,5.],
        &sim
    );

    assert_eq!(result, 0.0);
}

#[test]
fn test_kge_perfect() {
    let data: &[f64; 7] = &[1., 2., 3., 4., 5., 6., 7.];
    let s = hydromath::kge(data, data);
    assert_eq!(s, 1.0);
}

#[test]
fn test_kge_climatology() {
    let obs_data: &[f64; 5] = &[1., 2., 3., 4., 5.];
    let sim_data: &[f64; 5] = &[3., 3., 3., 3., 3.];
    let s = hydromath::kge(obs_data, sim_data);
    assert_eq!(s, 1.0f64 - (2.0f64).sqrt());
}

#[test]
fn test_kge_biased_climatology() {
    let obs_data: &[f64; 5] = &[1., 2., 3., 4., 5.];
    let sim_data: &[f64; 5] = &[6., 6., 6., 6., 6.];
    let s = hydromath::kge(obs_data, sim_data);
    assert_eq!(s, 1.0f64 - (3.0f64).sqrt());
}

#[test]
fn test_kge() {
    let obs_data: &[f64; 13] = &[1., 2., 3., 4., 5., 6., 7., 6., 5., 4., 3., 2., 1.];
    let sim_data: &[f64; 13] = &[1., 2., 3., 4., 5., 6., 6., 6., 5., 4., 3., 2., 1.];
    let s = hydromath::kge(obs_data, sim_data);
    assert_eq!(s, 0.93444263181747966307000297092599794268608093261719);
}
<commit_msg>Apply rustfmt to tests in tests/lib.rs<commit_after>extern crate hydromath;

#[test]
fn test_rmse() {
    let test_obs = &[13., 17., 18., 20., 24.];
    let test_sim = &[12., 15., 20., 22., 24.];

    let result = hydromath::rmse(test_obs, test_sim);
    assert_eq!(result, (2.6 as f64).sqrt());
}

#[test]
fn test_rmse_perfect() {
    let test_obs = &[1., 2., 3., 4., 5.];
    let test_sim = &[1., 2., 3., 4., 5.];

    let result = hydromath::rmse(test_obs, test_sim);
    assert_eq!(result, (0 as f64));
}

#[test]
fn test_rmse_bad() {
    let obs: [f64; 5] = [1., 2., 3., 4., 5.];

    let sum: f64 = obs.iter().fold(0f64, std::ops::Add::add);
    let m = sum / (obs.len() as f64);
    let sim = [m, m, m, m, m];
    let result = hydromath::rmse(&[1., 2., 3., 4., 5.], &sim);

    assert_eq!(result, (2.0 as f64).sqrt());
}

#[test]
fn test_mse() {
    let test_obs = &[13., 17., 18., 20., 24.];
    let test_sim = &[12., 15., 20., 22., 24.];

    let result = hydromath::mse(test_obs, test_sim);
    assert_eq!(result, 2.6);
}

#[test]
fn test_mse_perfect() {
    let result = hydromath::mse(&[1., 2., 3., 4., 5.], &[1., 2., 3., 4., 5.]);

    assert_eq!(result, 0.0);
}

#[test]
fn test_mse_bad() {
    let obs: [f64; 5] = [1., 2., 3., 4., 5.];

    let sum: f64 = obs.iter().fold(0f64, std::ops::Add::add);
    let m = sum / (obs.len() as f64);
    let sim = [m, m, m, m, m];
    let result = hydromath::mse(&[1., 2., 3., 4., 5.], &sim);

    assert_eq!(result, 2.0);
}

#[test]
fn test_nse() {
    let test_obs = &[5., 4., 6., 1., 3., 6., 8., 1., 7., 3., 4., 0.5];
    let test_sim = &[3., 4.5, 4., 2., 4., 5., 9., 2., 8., 3., 4., 0.8];

    let result = hydromath::nse(test_obs, test_sim);
    assert_eq!(result, 0.783479081472161);
}

#[test]
fn test_nse_perfect() {
    let result = hydromath::nse(&[1., 2., 3., 4., 5.], &[1., 2., 3., 4., 5.]);

    assert_eq!(result, 1.0);
}

#[test]
fn test_nse_bad() {
    let obs: [f64; 5] = [1., 2., 3., 4., 5.];

    let sum: f64 = obs.iter().fold(0f64, std::ops::Add::add);
    let m = sum / (obs.len() as f64);
    let sim = [m, m, m, m, m];
    let result = hydromath::nse(&[1., 2., 3., 4., 5.], &sim);

    assert_eq!(result, 0.0);
}

#[test]
fn test_kge_perfect() {
    let data: &[f64; 7] = &[1., 2., 3., 4., 5., 6., 7.];
    let s = hydromath::kge(data, data);
    assert_eq!(s, 1.0);
}

#[test]
fn test_kge_climatology() {
    let obs_data: &[f64; 5] = &[1., 2., 3., 4., 5.];
    let sim_data: &[f64; 5] = &[3., 3., 3., 3., 3.];
    let s = hydromath::kge(obs_data, sim_data);
    assert_eq!(s, 1.0f64 - (2.0f64).sqrt());
}

#[test]
fn test_kge_biased_climatology() {
    let obs_data: &[f64; 5] = &[1., 2., 3., 4., 5.];
    let sim_data: &[f64; 5] = &[6., 6., 6., 6., 6.];
    let s = hydromath::kge(obs_data, sim_data);
    assert_eq!(s, 1.0f64 - (3.0f64).sqrt());
}

#[test]
fn test_kge() {
    let obs_data: &[f64; 13] = &[1., 2., 3., 4., 5., 6., 7., 6., 5., 4., 3., 2., 1.];
    let sim_data: &[f64; 13] = &[1., 2., 3., 4., 5., 6., 6., 6., 5., 4., 3., 2., 1.];
    let s = hydromath::kge(obs_data, sim_data);
    assert_eq!(s, 0.93444263181747966307000297092599794268608093261719);
}
<|endoftext|>


<commit_before>
extern crate elastic_responses;

#[macro_use]
extern crate log;

#[macro_use]
extern crate json_str;

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

extern crate slog_stdlog;
extern crate slog_envlogger;

use elastic_responses::*;
use elastic_responses::error::*;
use std::fs::File;
use std::io::{Read, Cursor};

fn load_file_as_response(status: u16, p: &str) -> HttpResponse<Cursor<Vec<u8>>> {
    let mut f = File::open(p).unwrap();
    let mut s = Vec::new();
    f.read_to_end(&mut s).unwrap();

    HttpResponse::new(status, Cursor::new(s))
}

#[test]
fn test_parse_hits_simple() {
    let s = load_file_as_response(200, "tests/samples/hits_only.json");

    let deserialized = SearchResponse::from_response(s).unwrap();

    assert_eq!(deserialized.hits().into_iter().count(), 5);
}

#[test]
fn test_parse_simple_aggs() {
    let s = load_file_as_response(200, "tests/samples/aggregation_simple.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    assert_eq!(deserialized.aggs().into_iter().count(), 124);
}

#[test]
fn test_parse_3level_aggs() {
    let s = load_file_as_response(200, "tests/samples/aggregation_3level.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    assert_eq!(deserialized.aggs().into_iter().count(), 201);
}

#[test]
fn test_parse_3level_multichild_aggs() {
    let s = load_file_as_response(200, "tests/samples/aggregation_3level_multichild.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    let min = "min_ack_pkts_sent";
    let avg = "avg_ack_pkts_sent";
    let max = "max_ack_pkts_sent";
    let mut first = true;
    let mut count = 0;
    for i in deserialized.aggs().into_iter().take(500000) {
        count += 1;
        if first {
            assert!(i.contains_key(min));
            assert!(i.contains_key(max));
            assert!(i.contains_key(avg));
            first = false;
        }
    }
    assert_eq!(count, 201);
}

#[test]
fn test_parse_3level_multistats_aggs() {
    let s = load_file_as_response(200, "tests/samples/aggregation_3level_multistats.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    let min = "extstats_ack_pkts_sent_min";
    let avg = "stats_ack_pkts_sent_avg";
    let max = "extstats_ack_pkts_sent_max";
    let stddevu = "extstats_ack_pkts_sent_std_deviation_bounds_upper";
    let mut first = true;
    let mut count = 0;
    for i in deserialized.aggs().into_iter().take(500000) {
        count += 1;
        if first {
            assert!(i.contains_key(min));
            assert!(i.contains_key(max));
            assert!(i.contains_key(avg));
            assert!(i.contains_key(stddevu));
            first = false;
        }
    }
    assert_eq!(count, 61);
}

#[test]
fn test_parse_simple_aggs_no_empty_first_record() {
    let s = load_file_as_response(200, "tests/samples/aggregation_simple.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    let s = "timechart";
    let mut first = true;
    for i in deserialized.aggs().into_iter().take(50) {
        if first {
            assert!(i.contains_key(s));
            first = false;
        }
    }
}

#[test]
fn test_parse_found_doc_response() {
    let s = load_file_as_response(200, "tests/samples/get_found.json");
    let deserialized = GetResponse::from_response(s).unwrap();

    let id = deserialized.source
        .unwrap()
        .as_object()
        .and_then(|src| src.get("id"))
        .and_then(|id| id.as_u64());

    assert!(deserialized.found);
    assert_eq!("testindex", deserialized.index);
    assert_eq!("testtype", deserialized.ty);
    assert_eq!(Some(8), deserialized.version);
    assert_eq!(Some(1), id);
}

#[test]
fn test_parse_not_found_doc_response() {
    let s = load_file_as_response(404, "tests/samples/get_not_found.json");
    let deserialized = GetResponse::from_response(s).unwrap();

    assert!(!deserialized.found);
}

#[test]
fn test_parse_index_not_found_error() {
    let s = load_file_as_response(404, "tests/samples/error_index_not_found.json");
    let deserialized = GetResponse::from_response(s).unwrap_err();

    let valid = match deserialized {
        ResponseError::Api(ApiError::IndexNotFound { ref index })
        if index == "carrots" => true,
        _ => false
    };

    assert!(valid);
}

#[test]
fn test_parse_parsing_error() {
    let s = load_file_as_response(400, "tests/samples/error_parsing.json");
    let deserialized = SearchResponse::from_response(s).unwrap_err();

    let valid = match deserialized {
        ResponseError::Api(ApiError::Parsing { line: 2, col: 9, ref reason })
        if reason == "Unknown key for a START_OBJECT in [qry]." => true,
        _ => false
    };

    assert!(valid);
}

#[test]
fn test_parse_other_error() {
    let s = load_file_as_response(500, "tests/samples/error_other.json");
    let deserialized = SearchResponse::from_response(s).unwrap_err();

    let reason = match deserialized {
        ResponseError::Api(ApiError::Other(ref err)) => err.get("reason")
                                       .and_then(|reason| reason.as_str())
                                       .map(|reason| reason.to_owned()),
        _ => None
    };

    assert_eq!(Some(String::from("An error that we don't know about.")), reason);
}<commit_msg>add tests for value response<commit_after>
extern crate elastic_responses;

#[macro_use]
extern crate log;

#[macro_use]
extern crate json_str;

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

extern crate slog_stdlog;
extern crate slog_envlogger;

use elastic_responses::*;
use elastic_responses::error::*;
use serde_json::Value;
use std::fs::File;
use std::io::{Read, Cursor};

fn load_file_as_response(status: u16, p: &str) -> HttpResponse<Cursor<Vec<u8>>> {
    let mut f = File::open(p).unwrap();
    let mut s = Vec::new();
    f.read_to_end(&mut s).unwrap();

    HttpResponse::new(status, Cursor::new(s))
}

#[test]
fn test_read_response() {
    let mut res = HttpResponse::new(200, Cursor::new(vec![1, 2, 3]));

    let mut actual = Vec::new();
    res.read_to_end(&mut actual).unwrap();

    let expected = vec![1, 2, 3];

    assert_eq!(expected, actual);
}

#[test]
fn test_parse_hits_simple() {
    let s = load_file_as_response(200, "tests/samples/hits_only.json");

    let deserialized = SearchResponse::from_response(s).unwrap();

    assert_eq!(deserialized.hits().into_iter().count(), 5);
}

#[test]
fn test_parse_simple_aggs() {
    let s = load_file_as_response(200, "tests/samples/aggregation_simple.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    assert_eq!(deserialized.aggs().into_iter().count(), 124);
}

#[test]
fn test_parse_3level_aggs() {
    let s = load_file_as_response(200, "tests/samples/aggregation_3level.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    assert_eq!(deserialized.aggs().into_iter().count(), 201);
}

#[test]
fn test_parse_3level_multichild_aggs() {
    let s = load_file_as_response(200, "tests/samples/aggregation_3level_multichild.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    let min = "min_ack_pkts_sent";
    let avg = "avg_ack_pkts_sent";
    let max = "max_ack_pkts_sent";
    let mut first = true;
    let mut count = 0;
    for i in deserialized.aggs().into_iter().take(500000) {
        count += 1;
        if first {
            assert!(i.contains_key(min));
            assert!(i.contains_key(max));
            assert!(i.contains_key(avg));
            first = false;
        }
    }
    assert_eq!(count, 201);
}

#[test]
fn test_parse_3level_multistats_aggs() {
    let s = load_file_as_response(200, "tests/samples/aggregation_3level_multistats.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    let min = "extstats_ack_pkts_sent_min";
    let avg = "stats_ack_pkts_sent_avg";
    let max = "extstats_ack_pkts_sent_max";
    let stddevu = "extstats_ack_pkts_sent_std_deviation_bounds_upper";
    let mut first = true;
    let mut count = 0;
    for i in deserialized.aggs().into_iter().take(500000) {
        count += 1;
        if first {
            assert!(i.contains_key(min));
            assert!(i.contains_key(max));
            assert!(i.contains_key(avg));
            assert!(i.contains_key(stddevu));
            first = false;
        }
    }
    assert_eq!(count, 61);
}

#[test]
fn test_parse_simple_aggs_no_empty_first_record() {
    let s = load_file_as_response(200, "tests/samples/aggregation_simple.json");
    let deserialized = SearchResponse::from_response(s).unwrap();

    let s = "timechart";
    let mut first = true;
    for i in deserialized.aggs().into_iter().take(50) {
        if first {
            assert!(i.contains_key(s));
            first = false;
        }
    }
}

#[test]
fn test_parse_hits_simple_as_value() {
    let s = load_file_as_response(200, "tests/samples/hits_only.json");

    let deserialized = Value::from_response(s).unwrap();

    assert_eq!(deserialized["_shards"]["total"].as_u64().unwrap(), 5);
}

#[test]
fn test_parse_found_doc_response() {
    let s = load_file_as_response(200, "tests/samples/get_found.json");
    let deserialized = GetResponse::from_response(s).unwrap();

    let id = deserialized.source
        .unwrap()
        .as_object()
        .and_then(|src| src.get("id"))
        .and_then(|id| id.as_u64());

    assert!(deserialized.found);
    assert_eq!("testindex", deserialized.index);
    assert_eq!("testtype", deserialized.ty);
    assert_eq!(Some(8), deserialized.version);
    assert_eq!(Some(1), id);
}

#[test]
fn test_parse_not_found_doc_response() {
    let s = load_file_as_response(404, "tests/samples/get_not_found.json");
    let deserialized = GetResponse::from_response(s).unwrap();

    assert!(!deserialized.found);
}

#[test]
fn test_parse_index_not_found_error() {
    let s = load_file_as_response(404, "tests/samples/error_index_not_found.json");
    let deserialized = GetResponse::from_response(s).unwrap_err();

    let valid = match deserialized {
        ResponseError::Api(ApiError::IndexNotFound { ref index })
        if index == "carrots" => true,
        _ => false
    };

    assert!(valid);
}

#[test]
fn test_parse_parsing_error() {
    let s = load_file_as_response(400, "tests/samples/error_parsing.json");
    let deserialized = SearchResponse::from_response(s).unwrap_err();

    let valid = match deserialized {
        ResponseError::Api(ApiError::Parsing { line: 2, col: 9, ref reason })
        if reason == "Unknown key for a START_OBJECT in [qry]." => true,
        _ => false
    };

    assert!(valid);
}

#[test]
fn test_parse_other_error() {
    let s = load_file_as_response(500, "tests/samples/error_other.json");
    let deserialized = SearchResponse::from_response(s).unwrap_err();

    let reason = match deserialized {
        ResponseError::Api(ApiError::Other(ref err)) => err.get("reason")
                                       .and_then(|reason| reason.as_str())
                                       .map(|reason| reason.to_owned()),
        _ => None
    };

    assert_eq!(Some(String::from("An error that we don't know about.")), reason);
}<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Simple buddy-block allocator

mod math;
#[cfg(feature = "buddy_as_system")]
pub mod system;

use super::{ Framesque, Allocator };
use self::math::PowersOf2;

use core::mem;
use core::cmp::{max, min};
use core::ptr::Unique;

use intrusive::list::{List, Node};
use intrusive::rawlink::RawLink;

#[cfg(test)] mod test;
/// A `FreeList` is a list of unique free blocks
pub type FreeList = List<Unique<FreeBlock>, FreeBlock>;

/// A free block header stores a pointer to the next and previous free blocks.
pub struct FreeBlock { next: RawLink<FreeBlock>
                     , prev: RawLink<FreeBlock>
                     }

impl Framesque for FreeBlock {
    #[inline] fn as_ptr(&self) -> *mut u8 {
        unsafe { mem::transmute(self) } // HOPEFULLY this is good
    }
}

impl Node for FreeBlock {
    #[inline] fn prev(&self) -> &RawLink<FreeBlock> {
        &self.prev
    }
    #[inline] fn next(&self) -> &RawLink<FreeBlock> {
        &self.next
    }
    #[inline] fn prev_mut(&mut self) -> &mut RawLink<FreeBlock> {
        &mut self.prev
    }
    #[inline] fn next_mut(&mut self) -> &mut RawLink<FreeBlock> {
        &mut self.next
    }
}

// Variadic macro for taking the maximum of n > 2 numbers.
// because I'm lazy.
macro_rules! max {
    ($x:expr) => ($x);
    ($x:expr, $($xs:expr),+) => (max($x, max!($($xs),+)));
}

/// Structure with data for implementing the buddy block allocation strategy.
pub struct BuddyHeapAllocator<'a> {
    /// Address of the base of the heap. This must be aligned
    /// on a `MIN_ALIGN` boundary.
    pub start_addr: *mut u8
  , /// The allocator's free list
    free_lists: &'a mut [FreeList]
  , /// Number of blocks in the heap (must be a power of 2)
    pub heap_size: usize
  , /// Minimum block size
    pub min_block_size: usize
}

impl<'a> BuddyHeapAllocator<'a> {
    /// Construct a new `BuddyHeapAllocator`.
    ///
    /// # Arguments:
    /// + `start_addr`: a pointer to the start location of the heap
    /// + `free_lists`: an array of `FreeList`s. The cardinality
    ///     of the `free_lists` array should be equal to the maximum
    ///     allocateable order.
    /// + `heap_size`: the size of the heap (in bytes)
    ///
    /// # Returns:
    /// + A new `BuddyHeapAllocator`, obviously.
    pub unsafe fn new( start_addr: *mut u8
                     , free_lists: &'a mut [FreeList]
                     , heap_size: usize)
                     -> BuddyHeapAllocator<'a> {
        // Cache the number of free lists hopefully saving performance.
        let n_free_lists = free_lists.len();

        assert!( !start_addr.is_null()
                , "Heap start address cannot be null." );
        assert!( n_free_lists > 0
               , "Allocator must have at least one free list.");
        assert!( start_addr as usize & (::PAGE_SIZE-1) == 0
               , "Heap start address must be aligned on a 4k boundary.");

        let min_block_size = heap_size >> (n_free_lists - 1);

        assert!( heap_size >= min_block_size
               , "Heap must be large enough to contain at least one block.");
        assert!( min_block_size >= mem::size_of::<FreeBlock>()
               , "Minimum block size must be large enough to contain \
                  the free block header.");
        assert!( heap_size.is_pow2()
               , "Heap size must be a power of 2.");

    //    // We must have one free list per possible heap block size.
    //    assert_eq!(min_block_size *
    //               (2u32.pow(free_lists.len() as u32 - 1)) as usize,
    //               heap_size);

        // Zero out the free lists in case we were passed existing data.
        for list in free_lists.iter_mut() {
            *list = FreeList::new();
        }

        let mut heap
            = BuddyHeapAllocator { start_addr: start_addr
                                 , free_lists: free_lists
                                 , heap_size: heap_size
                                 , min_block_size: min_block_size
                                 };

        // the order needed to allocate the entire heap as a single block
        let root_order
            = heap.alloc_order(heap_size, 1)
                  .expect("Couldn't determine heap root allocation order!\
                           This should be (as far as I know) impossible.\
                           Something is seriously amiss.");

        // Push the entire heap onto the free lists as the first block.
        heap.push_block(start_addr, root_order);
        heap
    }

    /// Add a block of max order
    pub unsafe fn add_block(&mut self, block: *mut u8) {
        let order = self.free_lists.len() -1;
        self.push_block(block, order);
    }

    /// Computes the size of an allocation request.
    ///
    /// # Arguments
    /// + `size`: A `usize` containing the size of the request
    /// + `align`: A `usize` containing the alignment of the request
    ///
    /// # Returns
    /// + `None` if the request is invalid
    /// + `Some(usize)` containing the size needed if the request is valid
    #[inline]
    pub fn alloc_size(&self, size: usize, align: usize) -> Option<usize> {
        // Pre-check if this is a valid allocation request:
        //  - allocations must be aligned on power of 2 boundaries
        //  - we cannot allocate requests with alignments greater than the
        //    base alignment of the heap without jumping through a bunch of
        //    hoops.
        if !align.is_pow2() || align > ::PAGE_SIZE {
            None
        // If the request is valid, compute the size we need to allocate
        } else {
            // the allocation size for the request is the next power of 2
            // after the size of the request, the alignment of the request,
            // or the minimum block size (whichever is greatest).
            let alloc_size = max!(size, self.min_block_size, align).next_pow2();

            if alloc_size > self.heap_size {
                // if the calculated size is greater than the size of the heap,
                // we (obviously) cannot allocate this request.
                None
            } else {
                // otherwise, return the calculated size.
                Some(alloc_size)
            }
        }
    }

    /// Computes the order of an allocation request.
    ///
    /// The "order" of an allocation refers to the number of times we need to
    /// double the minimum block size to get a large enough block for that
    /// allocation.
    #[inline]
    pub fn alloc_order(&self, size: usize, align: usize) -> Option<usize> {
        trace!("TRACE: alloc_order() called");
        self.alloc_size(size, align)
            .map(|s| {
                trace!("TRACE in alloc_order(): alloc_size() returned {}", s);
                s.log2() - self.min_block_size.log2()
            })
    }

    /// Computes the size  allocated for a request of the given order.
    #[inline]
    fn order_alloc_size(&self, order: usize) -> usize {
        1 << (self.min_block_size.log2() + order)
    }

    #[inline]
    unsafe fn push_block(&mut self, ptr: *mut u8, order: usize) {
        self.free_lists[order]
            .push_front(Unique::new(ptr as *mut FreeBlock))
    }

    #[inline]
    unsafe fn pop_block(&mut self, order: usize) -> Option<*mut u8>{
        self.free_lists[order]
            .pop_front()
            .map(|block| block.get().as_ptr())
    }

    /// Splits a block
    unsafe fn split_block( &mut self
                         , block: *mut u8
                         , old_order: usize
                         , new_order: usize ) {
        trace!("split_block() was called, target order: {}.", new_order);

        assert!( new_order < old_order
               , "Cannot split a block larger than its current order!");
        assert!( old_order <= self.free_lists.len()
               , "Order is too large for this allocator!");

        let mut split_size = self.order_alloc_size(old_order);
        // let mut curr_order = order;
        for order in (new_order..old_order).rev() {
            split_size >>= 1;

            // let split_size = self.order_alloc_size(order);
            self.push_block(block.offset(split_size as isize), order);

            trace!( "split block successfully, order: {}, split size: {}"
                  , order, split_size );

        }
    }

    /// Finds the buddy block for a given block.
    ///
    /// # Arguments
    /// + `order`: the order of the block to find a buddy for
    /// + `block`: a pointer to the block to find a buddy for
    ///
    /// # Returns
    /// + `Some(*mut u8)` pointing to the buddy block if a buddy was found
    /// + `None` if the block was the size of the entire heap
    pub unsafe fn get_buddy( &self
                           , order: usize
                           , block: *mut u8)
                            -> Option<*mut u8> {
        // Determine the size of the block allocated for the given order
        let block_size = self.order_alloc_size(order);
        if block_size < self.heap_size {
            // Determine the block's position in the heap.
            let block_pos = (block as usize) - (self.start_addr as usize);
            // Calculate the block's buddy by XORing the block's position
            // in the heap with its size.
            Some(self.start_addr.offset((block_pos ^ block_size) as isize))
        } else {
            // If the block is the size of the entire heap, it (obviously)
            // cannot have a buddy block.
            None
        }
    }

    /// Finds and removes the target block from the free list.
    ///
    /// # Arguments
    /// + `order`: the order of the free list to remove the block from_raw
    /// + `block`: a pointer to the block to remove
    ///
    /// # Returns
    /// + `true` if the block was found and removed from the free List
    /// + `false` if the block was not found
    pub fn remove_block(&mut self, order: usize, block: *mut u8) -> bool {
        self.free_lists[order]
            .cursor_mut()
            .find_and_remove(|b| b as *const FreeBlock as *const u8 == block)
            .is_some()
    }
}

impl<'a> Allocator for BuddyHeapAllocator<'a> {

    /// Allocate a new block of size `size` on alignment `align`.
    ///
    /// # Arguments:
    /// + `size`: the amount of memory to allocate (in bytes)
    /// + `align`: the alignment for the allocation request
    ///
    /// # Returns:
    /// + `Some(*mut u8)` if the request was allocated successfully
    /// + `None` if the allocator is out of memory or if the request was
    ///     invalid.
    unsafe fn allocate( &mut self
                      , size: usize
                      , align: usize)
                      -> Option<*mut u8> {
        trace!("allocate() was called!");
        // First, compute the allocation order for this request
        self.alloc_order(size, align)
            .and_then(|order| if order > self.free_lists.len() - 1 { None }
                              else {Some(order)} )
            // If the allocation order is defined, then we try to allocate
            // a block of that order. Otherwise, the request is invalid.
            .and_then(|min_order| {
                trace!("in allocate(): min alloc order is {}", min_order);
                // Starting at the minimum possible order...
                // TODO: this is ugly and not FP, rewrite.
                for order in min_order..self.free_lists.len() {
                    if let Some(block) = self.pop_block(order) {
                        trace!( "in allocate(): found block");
                        if order > min_order {
                            trace!( "in allocate(): order {} is less than \
                                     minimum ({}), splitting."
                                  , order, min_order);
                            self.split_block(block, order, min_order);
                            trace!("in allocate(): split_block() done");

                        }
                        return Some(block)
                    }
                }
                None
            })
    }

    /// Release an allocated block of memory.
    ///
    /// The `size` and `align` parameters _must_ be the same as the original
    /// size and alignment of the frame being deallocated, otherwise our
    /// heap will become corrupted.
    ///
    /// # Arguments:
    /// + `frame`: a pointer to the block of memory to deallocate
    /// + `size`: the size of the block being deallocated
    /// + `align`: the alignment of the block being deallocated
    unsafe fn deallocate( &mut self
                        , block: *mut u8
                        , old_size: usize
                        , align: usize ) {
        let min_order = self.alloc_order(old_size, align)
                            .unwrap();

        // Check if the deallocated block's buddy block is also free.
        // If it is, merge the two blocks.
        let mut new_block = block;
        for order in min_order..self.free_lists.len() {
            // If there is a buddy for this block of the given order...
            if let Some(buddy) = self.get_buddy(order, block) {
                // ...and if the buddy was free...
                if self.remove_block(order, buddy) {
                    // ...merge the buddy with the new block (just use
                    // the lower address), and keep going.
                    new_block = min(new_block, buddy);
                    continue;
                }
            }
            // Otherwise, if we've run out of free buddies, push the new
            // merged block onto the free lsit and return.
            self.push_block(new_block, order);
            return;
        }
    }
}
<commit_msg>[sos-alloc] buddy allocator tests only work on Linux<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Simple buddy-block allocator

mod math;
#[cfg(feature = "buddy_as_system")]
pub mod system;

use super::{ Framesque, Allocator };
use self::math::PowersOf2;

use core::mem;
use core::cmp::{max, min};
use core::ptr::Unique;

use intrusive::list::{List, Node};
use intrusive::rawlink::RawLink;

#[cfg(target-os = "linux")]
#[cfg(test)]
mod test;

/// A `FreeList` is a list of unique free blocks
pub type FreeList = List<Unique<FreeBlock>, FreeBlock>;

/// A free block header stores a pointer to the next and previous free blocks.
pub struct FreeBlock { next: RawLink<FreeBlock>
                     , prev: RawLink<FreeBlock>
                     }

impl Framesque for FreeBlock {
    #[inline] fn as_ptr(&self) -> *mut u8 {
        unsafe { mem::transmute(self) } // HOPEFULLY this is good
    }
}

impl Node for FreeBlock {
    #[inline] fn prev(&self) -> &RawLink<FreeBlock> {
        &self.prev
    }
    #[inline] fn next(&self) -> &RawLink<FreeBlock> {
        &self.next
    }
    #[inline] fn prev_mut(&mut self) -> &mut RawLink<FreeBlock> {
        &mut self.prev
    }
    #[inline] fn next_mut(&mut self) -> &mut RawLink<FreeBlock> {
        &mut self.next
    }
}

// Variadic macro for taking the maximum of n > 2 numbers.
// because I'm lazy.
macro_rules! max {
    ($x:expr) => ($x);
    ($x:expr, $($xs:expr),+) => (max($x, max!($($xs),+)));
}

/// Structure with data for implementing the buddy block allocation strategy.
pub struct BuddyHeapAllocator<'a> {
    /// Address of the base of the heap. This must be aligned
    /// on a `MIN_ALIGN` boundary.
    pub start_addr: *mut u8
  , /// The allocator's free list
    free_lists: &'a mut [FreeList]
  , /// Number of blocks in the heap (must be a power of 2)
    pub heap_size: usize
  , /// Minimum block size
    pub min_block_size: usize
}

impl<'a> BuddyHeapAllocator<'a> {
    /// Construct a new `BuddyHeapAllocator`.
    ///
    /// # Arguments:
    /// + `start_addr`: a pointer to the start location of the heap
    /// + `free_lists`: an array of `FreeList`s. The cardinality
    ///     of the `free_lists` array should be equal to the maximum
    ///     allocateable order.
    /// + `heap_size`: the size of the heap (in bytes)
    ///
    /// # Returns:
    /// + A new `BuddyHeapAllocator`, obviously.
    pub unsafe fn new( start_addr: *mut u8
                     , free_lists: &'a mut [FreeList]
                     , heap_size: usize)
                     -> BuddyHeapAllocator<'a> {
        // Cache the number of free lists hopefully saving performance.
        let n_free_lists = free_lists.len();

        assert!( !start_addr.is_null()
                , "Heap start address cannot be null." );
        assert!( n_free_lists > 0
               , "Allocator must have at least one free list.");
        assert!( start_addr as usize & (::PAGE_SIZE-1) == 0
               , "Heap start address must be aligned on a 4k boundary.");

        let min_block_size = heap_size >> (n_free_lists - 1);

        assert!( heap_size >= min_block_size
               , "Heap must be large enough to contain at least one block.");
        assert!( min_block_size >= mem::size_of::<FreeBlock>()
               , "Minimum block size must be large enough to contain \
                  the free block header.");
        assert!( heap_size.is_pow2()
               , "Heap size must be a power of 2.");

    //    // We must have one free list per possible heap block size.
    //    assert_eq!(min_block_size *
    //               (2u32.pow(free_lists.len() as u32 - 1)) as usize,
    //               heap_size);

        // Zero out the free lists in case we were passed existing data.
        for list in free_lists.iter_mut() {
            *list = FreeList::new();
        }

        let mut heap
            = BuddyHeapAllocator { start_addr: start_addr
                                 , free_lists: free_lists
                                 , heap_size: heap_size
                                 , min_block_size: min_block_size
                                 };

        // the order needed to allocate the entire heap as a single block
        let root_order
            = heap.alloc_order(heap_size, 1)
                  .expect("Couldn't determine heap root allocation order!\
                           This should be (as far as I know) impossible.\
                           Something is seriously amiss.");

        // Push the entire heap onto the free lists as the first block.
        heap.push_block(start_addr, root_order);
        heap
    }

    /// Add a block of max order
    pub unsafe fn add_block(&mut self, block: *mut u8) {
        let order = self.free_lists.len() -1;
        self.push_block(block, order);
    }

    /// Computes the size of an allocation request.
    ///
    /// # Arguments
    /// + `size`: A `usize` containing the size of the request
    /// + `align`: A `usize` containing the alignment of the request
    ///
    /// # Returns
    /// + `None` if the request is invalid
    /// + `Some(usize)` containing the size needed if the request is valid
    #[inline]
    pub fn alloc_size(&self, size: usize, align: usize) -> Option<usize> {
        // Pre-check if this is a valid allocation request:
        //  - allocations must be aligned on power of 2 boundaries
        //  - we cannot allocate requests with alignments greater than the
        //    base alignment of the heap without jumping through a bunch of
        //    hoops.
        if !align.is_pow2() || align > ::PAGE_SIZE {
            None
        // If the request is valid, compute the size we need to allocate
        } else {
            // the allocation size for the request is the next power of 2
            // after the size of the request, the alignment of the request,
            // or the minimum block size (whichever is greatest).
            let alloc_size = max!(size, self.min_block_size, align).next_pow2();

            if alloc_size > self.heap_size {
                // if the calculated size is greater than the size of the heap,
                // we (obviously) cannot allocate this request.
                None
            } else {
                // otherwise, return the calculated size.
                Some(alloc_size)
            }
        }
    }

    /// Computes the order of an allocation request.
    ///
    /// The "order" of an allocation refers to the number of times we need to
    /// double the minimum block size to get a large enough block for that
    /// allocation.
    #[inline]
    pub fn alloc_order(&self, size: usize, align: usize) -> Option<usize> {
        trace!("TRACE: alloc_order() called");
        self.alloc_size(size, align)
            .map(|s| {
                trace!("TRACE in alloc_order(): alloc_size() returned {}", s);
                s.log2() - self.min_block_size.log2()
            })
    }

    /// Computes the size  allocated for a request of the given order.
    #[inline]
    fn order_alloc_size(&self, order: usize) -> usize {
        1 << (self.min_block_size.log2() + order)
    }

    #[inline]
    unsafe fn push_block(&mut self, ptr: *mut u8, order: usize) {
        self.free_lists[order]
            .push_front(Unique::new(ptr as *mut FreeBlock))
    }

    #[inline]
    unsafe fn pop_block(&mut self, order: usize) -> Option<*mut u8>{
        self.free_lists[order]
            .pop_front()
            .map(|block| block.get().as_ptr())
    }

    /// Splits a block
    unsafe fn split_block( &mut self
                         , block: *mut u8
                         , old_order: usize
                         , new_order: usize ) {
        trace!("split_block() was called, target order: {}.", new_order);

        assert!( new_order < old_order
               , "Cannot split a block larger than its current order!");
        assert!( old_order <= self.free_lists.len()
               , "Order is too large for this allocator!");

        let mut split_size = self.order_alloc_size(old_order);
        // let mut curr_order = order;
        for order in (new_order..old_order).rev() {
            split_size >>= 1;

            // let split_size = self.order_alloc_size(order);
            self.push_block(block.offset(split_size as isize), order);

            trace!( "split block successfully, order: {}, split size: {}"
                  , order, split_size );

        }
    }

    /// Finds the buddy block for a given block.
    ///
    /// # Arguments
    /// + `order`: the order of the block to find a buddy for
    /// + `block`: a pointer to the block to find a buddy for
    ///
    /// # Returns
    /// + `Some(*mut u8)` pointing to the buddy block if a buddy was found
    /// + `None` if the block was the size of the entire heap
    pub unsafe fn get_buddy( &self
                           , order: usize
                           , block: *mut u8)
                            -> Option<*mut u8> {
        // Determine the size of the block allocated for the given order
        let block_size = self.order_alloc_size(order);
        if block_size < self.heap_size {
            // Determine the block's position in the heap.
            let block_pos = (block as usize) - (self.start_addr as usize);
            // Calculate the block's buddy by XORing the block's position
            // in the heap with its size.
            Some(self.start_addr.offset((block_pos ^ block_size) as isize))
        } else {
            // If the block is the size of the entire heap, it (obviously)
            // cannot have a buddy block.
            None
        }
    }

    /// Finds and removes the target block from the free list.
    ///
    /// # Arguments
    /// + `order`: the order of the free list to remove the block from_raw
    /// + `block`: a pointer to the block to remove
    ///
    /// # Returns
    /// + `true` if the block was found and removed from the free List
    /// + `false` if the block was not found
    pub fn remove_block(&mut self, order: usize, block: *mut u8) -> bool {
        self.free_lists[order]
            .cursor_mut()
            .find_and_remove(|b| b as *const FreeBlock as *const u8 == block)
            .is_some()
    }
}

impl<'a> Allocator for BuddyHeapAllocator<'a> {

    /// Allocate a new block of size `size` on alignment `align`.
    ///
    /// # Arguments:
    /// + `size`: the amount of memory to allocate (in bytes)
    /// + `align`: the alignment for the allocation request
    ///
    /// # Returns:
    /// + `Some(*mut u8)` if the request was allocated successfully
    /// + `None` if the allocator is out of memory or if the request was
    ///     invalid.
    unsafe fn allocate( &mut self
                      , size: usize
                      , align: usize)
                      -> Option<*mut u8> {
        trace!("allocate() was called!");
        // First, compute the allocation order for this request
        self.alloc_order(size, align)
            .and_then(|order| if order > self.free_lists.len() - 1 { None }
                              else {Some(order)} )
            // If the allocation order is defined, then we try to allocate
            // a block of that order. Otherwise, the request is invalid.
            .and_then(|min_order| {
                trace!("in allocate(): min alloc order is {}", min_order);
                // Starting at the minimum possible order...
                // TODO: this is ugly and not FP, rewrite.
                for order in min_order..self.free_lists.len() {
                    if let Some(block) = self.pop_block(order) {
                        trace!( "in allocate(): found block");
                        if order > min_order {
                            trace!( "in allocate(): order {} is less than \
                                     minimum ({}), splitting."
                                  , order, min_order);
                            self.split_block(block, order, min_order);
                            trace!("in allocate(): split_block() done");

                        }
                        return Some(block)
                    }
                }
                None
            })
    }

    /// Release an allocated block of memory.
    ///
    /// The `size` and `align` parameters _must_ be the same as the original
    /// size and alignment of the frame being deallocated, otherwise our
    /// heap will become corrupted.
    ///
    /// # Arguments:
    /// + `frame`: a pointer to the block of memory to deallocate
    /// + `size`: the size of the block being deallocated
    /// + `align`: the alignment of the block being deallocated
    unsafe fn deallocate( &mut self
                        , block: *mut u8
                        , old_size: usize
                        , align: usize ) {
        let min_order = self.alloc_order(old_size, align)
                            .unwrap();

        // Check if the deallocated block's buddy block is also free.
        // If it is, merge the two blocks.
        let mut new_block = block;
        for order in min_order..self.free_lists.len() {
            // If there is a buddy for this block of the given order...
            if let Some(buddy) = self.get_buddy(order, block) {
                // ...and if the buddy was free...
                if self.remove_block(order, buddy) {
                    // ...merge the buddy with the new block (just use
                    // the lower address), and keep going.
                    new_block = min(new_block, buddy);
                    continue;
                }
            }
            // Otherwise, if we've run out of free buddies, push the new
            // merged block onto the free lsit and return.
            self.push_block(new_block, order);
            return;
        }
    }
}
<|endoftext|>