<commit_before>
use prelude::*;
use super::common::*;

use std::iter;

use idents;
use utils;
use tyhandlers::{Direction, TypeSystem};
use model;
use methodinfo::ComMethodInfo;

extern crate proc_macro;
use syn::*;

/// Expands the `com_interface` attribute.
///
/// The attribute expansion results in the following items:
///
/// - Global IID for the interface.
/// - Virtual table struct definition for the interface.
/// - Implementation for the delegating methods when calling the COM interface
///   from Rust.
pub fn expand_com_interface(
    attr_tokens: TokenStreamNightly,
    item_tokens: TokenStreamNightly,
) -> Result<TokenStreamNightly, model::ParseError>
{
    // Parse the attribute.
    let mut output = vec![];
    let itf = model::ComInterface::parse(
            &lib_name(),
            attr_tokens.into(),
            &item_tokens.to_string() )?;
    let itf_ident = itf.name();

    // Impls for Rust-to-COM calls using Automation type system.
    for ( &ts, itf_variant ) in itf.variants() {
        process_itf_variant( &itf, ts, itf_variant, &mut output );
    }

    // If this is a trait based interface, implement Deref for ComItf.
    //
    // Trait based interfaces can always be Deref'd from ComItf into &Trait.
    // Struct based interfaces can only be Deref'd if the struct has the
    // interface in its vtable list. This is decided in the com_class
    // attribute.
    if itf.item_type() == utils::InterfaceType::Trait {
        output.push( quote!(
            impl ::std::ops::Deref for ::intercom::ComItf< #itf_ident > {
                type Target = #itf_ident;
                fn deref( &self ) -> &Self::Target {
                    self
                }
            }
        ) );
    }

    Ok( tokens_to_tokenstream( item_tokens, output ) )
}

/// Processes the interface type system variant.
///
/// # Arguments
///
/// * `itf` - Interface details.
/// * `ts` - Type system the variant represents.
/// * `itf_variant` - Interface variant details.
/// * `output` - Output emitted by the attribute macro.
fn process_itf_variant(
    itf : &model::ComInterface,
    ts : TypeSystem,
    itf_variant : &model::ComInterfaceVariant,
    output : &mut Vec<TokenStream>,
) {

    let itf_ident = itf.name();
    let visibility = itf.visibility();
    let iid_ident = idents::iid( itf_variant.unique_name() );
    let vtable_ident = idents::vtable_struct( itf_variant.unique_name() );

    // IID_IInterface GUID.
    let iid_tokens = utils::get_guid_tokens( itf_variant.iid() );
    let iid_doc = format!( "`{}` interface ID.", itf_ident );
    output.push( quote!(
        #[doc = #iid_doc]
        #[allow(non_upper_case_globals)]
        #visibility const #iid_ident : ::intercom::IID = #iid_tokens;
    ) );

    // Create a vector for the virtual table fields and insert the base
    // interface virtual table in it if required.
    let mut vtbl_fields = vec![];
    if let Some( ref base ) = *itf.base_interface() {
        let vtbl = match base.to_string().as_ref() {
            "IUnknown" => quote!( ::intercom::IUnknownVtbl ),
            _ => { let vtbl = idents::vtable_struct( &base ); quote!( #vtbl ) }
        };
        vtbl_fields.push( quote!( pub __base : #vtbl ) );
    }

    // Gather all the trait methods for the remaining vtable fields.
    let calling_convention = get_calling_convetion();
    for method_info in itf_variant.methods() {

        let method_ident = &method_info.unique_name;
        let in_out_args = method_info.raw_com_args()
                .into_iter()
                .map( |com_arg| {
                    let name = &com_arg.name;
                    let com_ty = &com_arg.handler.com_ty();
                    let dir = match com_arg.dir {
                        Direction::In => quote!(),
                        Direction::Out | Direction::Retval => quote!( *mut )
                    };
                    quote!( #name : #dir #com_ty )
                } );
        let self_arg = quote!( self_vtable : ::intercom::RawComPtr );
        let args = iter::once( self_arg ).chain( in_out_args );

        // Create the vtable field and add it to the vector of fields.
        let ret_ty = method_info.returnhandler.com_ty();
        vtbl_fields.push( quote!(
            pub #method_ident :
                unsafe extern #calling_convention fn( #( #args ),* ) -> #ret_ty
        ) );
    }

    // Create the vtable. We've already gathered all the vtable method
    // pointer fields so defining the struct is simple enough.
    output.push( quote!(
        #[allow(non_camel_case_types)]
        #[repr(C)]
        #[doc(hidden)]
        #visibility struct #vtable_ident { #( #vtbl_fields, )* }
    ) );

    // COM delegation implementation.
    // This is done only for the primary interface, whic is currently always the Automation
    // interface.
    if ts == TypeSystem::Automation {

        // IidOf trait impl.
        let iidof_doc = format!( "Returns `{}`.", iid_ident );
        output.push( quote!(
            impl ::intercom::IidOf for #itf_ident {
                #[doc = #iidof_doc]
                fn iid() -> &'static ::intercom::IID {
                    & #iid_tokens
                }
            }
        ) );

        // If this is a trait (as opposed to an implicit struct `impl`), include
        // the Rust-to-COM call implementations.
        //
        // If the [com_interface] is on an implicit struct `impl` we'd end up with
        // `impl StructName for intercom::ComItf<StructName>`, which is invalid
        // syntax when `StructName` is struct instead of a trait.
        if itf.item_type() == utils::InterfaceType::Trait {

            // Gather method implementations.
            let impls = itf_variant.methods().iter()
                    .map( |m| rust_to_com_delegate( m, &vtable_ident ) );

            let unsafety = if itf.is_unsafe() { quote!( unsafe ) } else { quote!() };
            output.push( quote!(
                #unsafety impl #itf_ident for ::intercom::ComItf< #itf_ident > {
                    #( #impls )*
                }
            ) );
        }
    }
}

/// Creates the functions responsible for delegating calls from Rust to COM
/// interfaces.
///
/// # Arguments
///
/// * `method_info` - Method to delegate.
/// * `vtable_ident` - Vtable to use for the delegation.
fn rust_to_com_delegate(
    method_info : &ComMethodInfo,
    vtable_ident : &Ident,
) -> TokenStream {

    // Format the method arguments into tokens.
    let impl_args = method_info.args.iter().map( |ca| {
        let name = &ca.name;
        let ty = &ca.ty;
        quote!( #name : #ty )
    } );

    // The COM out-arguments that mirror the Rust return value will
    // require temporary variables during the COM call. Format their
    // declarations.
    let out_arg_declarations = method_info.returnhandler.com_out_args()
            .iter()
            .map( |ca| {
                let ident = &ca.name;
                let ty = &ca.handler.com_ty();
                let default = ca.handler.default_value();
                quote!( let mut #ident : #ty = #default; )
            } ).collect::<Vec<_>>();

    // Format the in and out parameters for the COM call.
    let ( temporaries, params ) : ( Vec<_>, Vec<_> ) = method_info.raw_com_args()
            .into_iter()
            .map( |com_arg| {
                let name = com_arg.name;
                match com_arg.dir {
                    Direction::In => {
                        let param = com_arg.handler.rust_to_com( &name );
                        ( param.temporary, param.value )
                    },
                    Direction::Out | Direction::Retval
                        => ( None, quote!( &mut #name ) ),
                }
            } )
            .unzip();

    // Combine the parameters into the final parameter list.
    // This includes the 'this' pointer and both the IN and OUT
    // parameters.
    let params = iter::once( quote!( comptr ) ).chain( params );

    // Create the return statement.
    let return_ident = Ident::new( "__result", Span::call_site() );
    let return_statement = method_info
            .returnhandler
            .com_to_rust_return( &return_ident );

    // Resolve some of the fields needed for quote.
    let method_ident = &method_info.unique_name;
    let method_rust_ident = &method_info.display_name;
    let self_arg = &method_info.rust_self_arg;
    let return_ty = &method_info.rust_return_ty;
    let unsafety = if method_info.is_unsafe { quote!( unsafe ) } else { quote!() };

    // Construct the final method.
    quote!(
        #unsafety fn #method_rust_ident(
            #self_arg, #( #impl_args ),*
        ) -> #return_ty
        {
            #[allow(unused_imports)]
            use ::intercom::ComInto;

            let comptr = ::intercom::ComItf::ptr( self );
            let vtbl = comptr as *const *const #vtable_ident;

            #( #temporaries )*

            // Use an IIFE to act as a try/catch block. The various template
            // substitutions might end up using ?'s for error handling. The IIFE allows
            // us to handle the results here immediately.
            #[allow(unused_unsafe)]  // The fn itself _might_ be unsafe.
            let result : Result< #return_ty, ::intercom::ComError > = ( || unsafe {
                #( #out_arg_declarations )*;
                let #return_ident = ((**vtbl).#method_ident)( #( #params ),* );

                Ok( { #return_statement } )
            } )();

            #[allow(unused_imports)]
            use ::intercom::ErrorValue;
            match result {
                Ok( v ) => v,
                Err( err ) => < #return_ty as ErrorValue >::from_error(
                        ::intercom::return_hresult( err ) ),
            }
        }
    )
}
<commit_msg>Restore IidOf implementation<commit_after>
use prelude::*;
use super::common::*;

use std::iter;

use idents;
use utils;
use tyhandlers::{Direction, TypeSystem};
use model;
use methodinfo::ComMethodInfo;

extern crate proc_macro;
use syn::*;

/// Expands the `com_interface` attribute.
///
/// The attribute expansion results in the following items:
///
/// - Global IID for the interface.
/// - Virtual table struct definition for the interface.
/// - Implementation for the delegating methods when calling the COM interface
///   from Rust.
pub fn expand_com_interface(
    attr_tokens: TokenStreamNightly,
    item_tokens: TokenStreamNightly,
) -> Result<TokenStreamNightly, model::ParseError>
{
    // Parse the attribute.
    let mut output = vec![];
    let itf = model::ComInterface::parse(
            &lib_name(),
            attr_tokens.into(),
            &item_tokens.to_string() )?;
    let itf_ident = itf.name();

    // Impls for Rust-to-COM calls using Automation type system.
    for ( &ts, itf_variant ) in itf.variants() {
        process_itf_variant( &itf, ts, itf_variant, &mut output );
    }

    // If this is a trait based interface, implement Deref for ComItf.
    //
    // Trait based interfaces can always be Deref'd from ComItf into &Trait.
    // Struct based interfaces can only be Deref'd if the struct has the
    // interface in its vtable list. This is decided in the com_class
    // attribute.
    if itf.item_type() == utils::InterfaceType::Trait {
        output.push( quote!(
            impl ::std::ops::Deref for ::intercom::ComItf< #itf_ident > {
                type Target = #itf_ident;
                fn deref( &self ) -> &Self::Target {
                    self
                }
            }
        ) );
    }

    Ok( tokens_to_tokenstream( item_tokens, output ) )
}

/// Processes the interface type system variant.
///
/// # Arguments
///
/// * `itf` - Interface details.
/// * `ts` - Type system the variant represents.
/// * `itf_variant` - Interface variant details.
/// * `output` - Output emitted by the attribute macro.
fn process_itf_variant(
    itf : &model::ComInterface,
    ts : TypeSystem,
    itf_variant : &model::ComInterfaceVariant,
    output : &mut Vec<TokenStream>,
) {

    let itf_ident = itf.name();
    let visibility = itf.visibility();
    let iid_ident = idents::iid( itf_variant.unique_name() );
    let vtable_ident = idents::vtable_struct( itf_variant.unique_name() );

    // IID_IInterface GUID.
    let iid_tokens = utils::get_guid_tokens( itf_variant.iid() );
    let iid_doc = format!( "`{}` interface ID.", itf_ident );
    output.push( quote!(
        #[doc = #iid_doc]
        #[allow(non_upper_case_globals)]
        #visibility const #iid_ident : ::intercom::IID = #iid_tokens;
    ) );

    // Create a vector for the virtual table fields and insert the base
    // interface virtual table in it if required.
    let mut vtbl_fields = vec![];
    if let Some( ref base ) = *itf.base_interface() {
        let vtbl = match base.to_string().as_ref() {
            "IUnknown" => quote!( ::intercom::IUnknownVtbl ),
            _ => { let vtbl = idents::vtable_struct( &base ); quote!( #vtbl ) }
        };
        vtbl_fields.push( quote!( pub __base : #vtbl ) );
    }

    // Gather all the trait methods for the remaining vtable fields.
    let calling_convention = get_calling_convetion();
    for method_info in itf_variant.methods() {

        let method_ident = &method_info.unique_name;
        let in_out_args = method_info.raw_com_args()
                .into_iter()
                .map( |com_arg| {
                    let name = &com_arg.name;
                    let com_ty = &com_arg.handler.com_ty();
                    let dir = match com_arg.dir {
                        Direction::In => quote!(),
                        Direction::Out | Direction::Retval => quote!( *mut )
                    };
                    quote!( #name : #dir #com_ty )
                } );
        let self_arg = quote!( self_vtable : ::intercom::RawComPtr );
        let args = iter::once( self_arg ).chain( in_out_args );

        // Create the vtable field and add it to the vector of fields.
        let ret_ty = method_info.returnhandler.com_ty();
        vtbl_fields.push( quote!(
            pub #method_ident :
                unsafe extern #calling_convention fn( #( #args ),* ) -> #ret_ty
        ) );
    }

    // Create the vtable. We've already gathered all the vtable method
    // pointer fields so defining the struct is simple enough.
    output.push( quote!(
        #[allow(non_camel_case_types)]
        #[repr(C)]
        #[doc(hidden)]
        #visibility struct #vtable_ident { #( #vtbl_fields, )* }
    ) );

    // COM delegation implementation.
    // This is done only for the primary interface, whic is currently always the Automation
    // interface.
    if ts == TypeSystem::Automation {

        // IidOf trait impl.
        let iidof_doc = format!( "Returns `{}`.", iid_ident );
        output.push( quote!(
            impl ::intercom::IidOf for #itf_ident {
                #[doc = #iidof_doc]
                fn iid() -> &'static ::intercom::IID {
                    & #iid_ident
                }
            }
        ) );

        // If this is a trait (as opposed to an implicit struct `impl`), include
        // the Rust-to-COM call implementations.
        //
        // If the [com_interface] is on an implicit struct `impl` we'd end up with
        // `impl StructName for intercom::ComItf<StructName>`, which is invalid
        // syntax when `StructName` is struct instead of a trait.
        if itf.item_type() == utils::InterfaceType::Trait {

            // Gather method implementations.
            let impls = itf_variant.methods().iter()
                    .map( |m| rust_to_com_delegate( m, &vtable_ident ) );

            let unsafety = if itf.is_unsafe() { quote!( unsafe ) } else { quote!() };
            output.push( quote!(
                #unsafety impl #itf_ident for ::intercom::ComItf< #itf_ident > {
                    #( #impls )*
                }
            ) );
        }
    }
}

/// Creates the functions responsible for delegating calls from Rust to COM
/// interfaces.
///
/// # Arguments
///
/// * `method_info` - Method to delegate.
/// * `vtable_ident` - Vtable to use for the delegation.
fn rust_to_com_delegate(
    method_info : &ComMethodInfo,
    vtable_ident : &Ident,
) -> TokenStream {

    // Format the method arguments into tokens.
    let impl_args = method_info.args.iter().map( |ca| {
        let name = &ca.name;
        let ty = &ca.ty;
        quote!( #name : #ty )
    } );

    // The COM out-arguments that mirror the Rust return value will
    // require temporary variables during the COM call. Format their
    // declarations.
    let out_arg_declarations = method_info.returnhandler.com_out_args()
            .iter()
            .map( |ca| {
                let ident = &ca.name;
                let ty = &ca.handler.com_ty();
                let default = ca.handler.default_value();
                quote!( let mut #ident : #ty = #default; )
            } ).collect::<Vec<_>>();

    // Format the in and out parameters for the COM call.
    let ( temporaries, params ) : ( Vec<_>, Vec<_> ) = method_info.raw_com_args()
            .into_iter()
            .map( |com_arg| {
                let name = com_arg.name;
                match com_arg.dir {
                    Direction::In => {
                        let param = com_arg.handler.rust_to_com( &name );
                        ( param.temporary, param.value )
                    },
                    Direction::Out | Direction::Retval
                        => ( None, quote!( &mut #name ) ),
                }
            } )
            .unzip();

    // Combine the parameters into the final parameter list.
    // This includes the 'this' pointer and both the IN and OUT
    // parameters.
    let params = iter::once( quote!( comptr ) ).chain( params );

    // Create the return statement.
    let return_ident = Ident::new( "__result", Span::call_site() );
    let return_statement = method_info
            .returnhandler
            .com_to_rust_return( &return_ident );

    // Resolve some of the fields needed for quote.
    let method_ident = &method_info.unique_name;
    let method_rust_ident = &method_info.display_name;
    let self_arg = &method_info.rust_self_arg;
    let return_ty = &method_info.rust_return_ty;
    let unsafety = if method_info.is_unsafe { quote!( unsafe ) } else { quote!() };

    // Construct the final method.
    quote!(
        #unsafety fn #method_rust_ident(
            #self_arg, #( #impl_args ),*
        ) -> #return_ty
        {
            #[allow(unused_imports)]
            use ::intercom::ComInto;

            let comptr = ::intercom::ComItf::ptr( self );
            let vtbl = comptr as *const *const #vtable_ident;

            #( #temporaries )*

            // Use an IIFE to act as a try/catch block. The various template
            // substitutions might end up using ?'s for error handling. The IIFE allows
            // us to handle the results here immediately.
            #[allow(unused_unsafe)]  // The fn itself _might_ be unsafe.
            let result : Result< #return_ty, ::intercom::ComError > = ( || unsafe {
                #( #out_arg_declarations )*;
                let #return_ident = ((**vtbl).#method_ident)( #( #params ),* );

                Ok( { #return_statement } )
            } )();

            #[allow(unused_imports)]
            use ::intercom::ErrorValue;
            match result {
                Ok( v ) => v,
                Err( err ) => < #return_ty as ErrorValue >::from_error(
                        ::intercom::return_hresult( err ) ),
            }
        }
    )
}
<|endoftext|>


<commit_before>use specs;

#[derive(Debug)]
pub struct CharacterSprite {
  pub character_idx: usize,
  pub character_fire_idx: usize,
}

impl CharacterSprite {
  pub fn new() -> CharacterSprite {
    CharacterSprite {
      character_idx: 0,
      character_fire_idx: 0,
    }
  }

  pub fn update_run(&mut self) {
    if self.character_idx < 13 {
      self.character_idx += 1;
    } else {
      self.character_idx = 0;
    }
    self.character_fire_idx = 0;
  }

  pub fn update_fire(&mut self) {
    if self.character_fire_idx < 4 {
      self.character_fire_idx += 1;
    } else {
      self.character_fire_idx = 0;
    }
  }
}

impl specs::Component for CharacterSprite {
  type Storage = specs::VecStorage<CharacterSprite>;
}

#[derive(Debug)]
pub struct ZombieSprite {
  pub zombie_idx: usize,
}

impl ZombieSprite {
  pub fn new() -> ZombieSprite {
    ZombieSprite {
      zombie_idx: 0,
    }
  }

  pub fn update_walk(&mut self) {
    if self.zombie_idx < 8 {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_still(&mut self) {
    if self.zombie_idx < 3 {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }
}

impl specs::Component for ZombieSprite {
  type Storage = specs::VecStorage<ZombieSprite>;
}

#[derive(Debug)]
pub struct CritterData {
  pub data: [f32; 4]
}

impl CritterData {
  pub fn new(data: [f32; 4]) -> CritterData {
    CritterData { data }
  }
}
<commit_msg>Update index-off-by-one issue<commit_after>use specs;

#[derive(Debug)]
pub struct CharacterSprite {
  pub character_idx: usize,
  pub character_fire_idx: usize,
}

impl CharacterSprite {
  pub fn new() -> CharacterSprite {
    CharacterSprite {
      character_idx: 0,
      character_fire_idx: 0,
    }
  }

  pub fn update_run(&mut self) {
    if self.character_idx < 12 {
      self.character_idx += 1;
    } else {
      self.character_idx = 0;
    }
    self.character_fire_idx = 0;
  }

  pub fn update_fire(&mut self) {
    if self.character_fire_idx < 3 {
      self.character_fire_idx += 1;
    } else {
      self.character_fire_idx = 0;
    }
  }
}

impl specs::Component for CharacterSprite {
  type Storage = specs::VecStorage<CharacterSprite>;
}

#[derive(Debug)]
pub struct ZombieSprite {
  pub zombie_idx: usize,
}

impl ZombieSprite {
  pub fn new() -> ZombieSprite {
    ZombieSprite {
      zombie_idx: 0,
    }
  }

  pub fn update_walk(&mut self) {
    if self.zombie_idx < 7 {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_still(&mut self) {
    if self.zombie_idx < 3 {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }
}

impl specs::Component for ZombieSprite {
  type Storage = specs::VecStorage<ZombieSprite>;
}

#[derive(Debug)]
pub struct CritterData {
  pub data: [f32; 4]
}

impl CritterData {
  pub fn new(data: [f32; 4]) -> CritterData {
    CritterData { data }
  }
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate lzf;
extern crate test;
use test::Bencher;

#[bench]
fn bench_basic_lzf_compression_decompression(b: &mut Bencher) {
    b.iter(|| {
        let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.";

        let compressed = lzf::compress(lorem.as_bytes()).unwrap();
        let _ = lzf::decompress(&compressed, lorem.len());
    })
}

#[bench]
fn bench_basic_lzf_decompression(b: &mut Bencher) {
    b.iter(|| {
        let lorem = [
            31, 76, 111, 114, 101, 109, 32, 105, 112, 115, 117, 109, 32, 100, 111, 108, 111, 114,
            32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 99, 111, 110, 115, 4, 101, 116, 101,
            116, 117, 32, 20, 1, 97, 100, 32, 35, 31, 99, 105, 110, 103, 32, 101, 108, 105, 116,
            114, 44, 32, 115, 101, 100, 32, 100, 105, 97, 109, 32, 110, 111, 110, 117, 109, 121,
            32, 101, 105, 114, 109, 6, 111, 100, 32, 116, 101, 109, 112, 32, 68, 14, 105, 110, 118,
            105, 100, 117, 110, 116, 32, 117, 116, 32, 108, 97, 98, 32, 100, 2, 32, 101, 116, 128,
            96, 13, 101, 32, 109, 97, 103, 110, 97, 32, 97, 108, 105, 113, 117, 121, 32, 64, 2,
            101, 114, 97, 32, 108, 224, 0, 79, 20, 118, 111, 108, 117, 112, 116, 117, 97, 46, 32,
            65, 116, 32, 118, 101, 114, 111, 32, 101, 111, 115, 64, 61, 4, 97, 99, 99, 117, 115,
            64, 47, 9, 116, 32, 106, 117, 115, 116, 111, 32, 100, 117, 32, 3, 64, 179, 0, 101, 96,
            31, 10, 101, 97, 32, 114, 101, 98, 117, 109, 46, 32, 83, 32, 180, 1, 32, 99, 32, 167,
            16, 97, 32, 107, 97, 115, 100, 32, 103, 117, 98, 101, 114, 103, 114, 101, 110, 44, 32,
            173, 32, 105, 7, 97, 32, 116, 97, 107, 105, 109, 97, 32, 31, 5, 115, 97, 110, 99, 116,
            117, 32, 63, 3, 115, 116, 32, 76, 32, 73, 225, 13, 11, 0, 46, 224, 18, 27, 225, 118,
            39, 1, 97, 46,
        ];

        let _ = lzf::decompress(&lorem, 451);
    })
}

#[bench]
fn bench_basic_lzf_compression(b: &mut Bencher) {
    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.";
    b.iter(|| {
        let _ = lzf::compress(lorem.as_bytes()).unwrap();
    })
}
<commit_msg>No extern crate<commit_after>#![feature(test)]

extern crate test;

use test::Bencher;

#[bench]
fn bench_basic_lzf_compression_decompression(b: &mut Bencher) {
    b.iter(|| {
        let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.";

        let compressed = lzf::compress(lorem.as_bytes()).unwrap();
        let _ = lzf::decompress(&compressed, lorem.len());
    })
}

#[bench]
fn bench_basic_lzf_decompression(b: &mut Bencher) {
    b.iter(|| {
        let lorem = [
            31, 76, 111, 114, 101, 109, 32, 105, 112, 115, 117, 109, 32, 100, 111, 108, 111, 114,
            32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 99, 111, 110, 115, 4, 101, 116, 101,
            116, 117, 32, 20, 1, 97, 100, 32, 35, 31, 99, 105, 110, 103, 32, 101, 108, 105, 116,
            114, 44, 32, 115, 101, 100, 32, 100, 105, 97, 109, 32, 110, 111, 110, 117, 109, 121,
            32, 101, 105, 114, 109, 6, 111, 100, 32, 116, 101, 109, 112, 32, 68, 14, 105, 110, 118,
            105, 100, 117, 110, 116, 32, 117, 116, 32, 108, 97, 98, 32, 100, 2, 32, 101, 116, 128,
            96, 13, 101, 32, 109, 97, 103, 110, 97, 32, 97, 108, 105, 113, 117, 121, 32, 64, 2,
            101, 114, 97, 32, 108, 224, 0, 79, 20, 118, 111, 108, 117, 112, 116, 117, 97, 46, 32,
            65, 116, 32, 118, 101, 114, 111, 32, 101, 111, 115, 64, 61, 4, 97, 99, 99, 117, 115,
            64, 47, 9, 116, 32, 106, 117, 115, 116, 111, 32, 100, 117, 32, 3, 64, 179, 0, 101, 96,
            31, 10, 101, 97, 32, 114, 101, 98, 117, 109, 46, 32, 83, 32, 180, 1, 32, 99, 32, 167,
            16, 97, 32, 107, 97, 115, 100, 32, 103, 117, 98, 101, 114, 103, 114, 101, 110, 44, 32,
            173, 32, 105, 7, 97, 32, 116, 97, 107, 105, 109, 97, 32, 31, 5, 115, 97, 110, 99, 116,
            117, 32, 63, 3, 115, 116, 32, 76, 32, 73, 225, 13, 11, 0, 46, 224, 18, 27, 225, 118,
            39, 1, 97, 46,
        ];

        let _ = lzf::decompress(&lorem, 451);
    })
}

#[bench]
fn bench_basic_lzf_compression(b: &mut Bencher) {
    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.";
    b.iter(|| {
        let _ = lzf::compress(lorem.as_bytes()).unwrap();
    })
}
<|endoftext|>


<commit_before>mod statistics;
mod planner;

use kite::query::Query;
use kite::collectors::{Collector, DocumentMatch};
use byteorder::{ByteOrder, BigEndian};

use super::RocksDBIndexReader;
use doc_id_set::DocIdSet;
use segment::{Segment, SegmentReadError};
use search::statistics::{StatisticsReader, RocksDBStatisticsReader};
use search::planner::{SearchPlan, plan_query};
use search::planner::boolean_query::BooleanQueryOp;
use search::planner::score_function::{CombinatorScorer, ScoreFunctionOp};

impl<'a> RocksDBIndexReader<'a> {
    fn search_segment_boolean_phase<S: Segment>(&self, boolean_query: &Vec<BooleanQueryOp>, is_negated: bool, segment: &S) -> Result<DocIdSet, SegmentReadError> {
        // Execute boolean query
        let mut stack = Vec::new();
        for op in boolean_query.iter() {
            match *op {
                BooleanQueryOp::PushEmpty => {
                    stack.push(DocIdSet::new_filled(0));
                }
                BooleanQueryOp::PushFull => {
                    stack.push(DocIdSet::new_filled(65536));
                }
                BooleanQueryOp::PushTermDirectory(field_ref, term_ref) => {
                    match try!(segment.load_term_directory(field_ref, term_ref)) {
                        Some(doc_id_set) => stack.push(doc_id_set),
                        None => stack.push(DocIdSet::new_filled(0)),
                    }
                }
                BooleanQueryOp::PushDeletionList => {
                     match try!(segment.load_deletion_list()) {
                        Some(doc_id_set) => stack.push(doc_id_set),
                        None => stack.push(DocIdSet::new_filled(0)),
                    }
                }
                BooleanQueryOp::And => {
                    let b = stack.pop().expect("boolean query executor: stack underflow");
                    let a = stack.pop().expect("boolean query executor: stack underflow");
                    stack.push(a.intersection(&b));
                }
                BooleanQueryOp::Or => {
                    let b = stack.pop().expect("boolean query executor: stack underflow");
                    let a = stack.pop().expect("boolean query executor: stack underflow");
                    stack.push(a.union(&b));
                }
                BooleanQueryOp::AndNot => {
                    let b = stack.pop().expect("boolean query executor: stack underflow");
                    let a = stack.pop().expect("boolean query executor: stack underflow");
                    stack.push(a.exclusion(&b));
                }
            }
        }

        if !stack.len() == 1 {
            // This shouldn't be possible unless there's a bug in the planner
            panic!("boolean query executor: stack size too big ({})", stack.len());
        }
        let mut matches = stack.pop().unwrap();

        // Invert the list if the query is negated
        if is_negated {
            let total_docs = try!(segment.load_statistic(b"total_docs")).unwrap_or(0);
            let all_docs = DocIdSet::new_filled(total_docs as u32);
            matches = all_docs.exclusion(&matches);
        }

        Ok(matches)
    }

    fn score_doc<S: Segment, R: StatisticsReader>(&self, doc_id: u16, score_function: &Vec<ScoreFunctionOp>, segment: &S, mut stats: &mut R) -> Result<f64, SegmentReadError> {
        // Execute score function
        let mut stack = Vec::new();
        for op in score_function.iter() {
            match *op {
                ScoreFunctionOp::Literal(val) => stack.push(val),
                ScoreFunctionOp::TermScorer(field_ref, term_ref, ref scorer) => {
                    // TODO: Check this isn't really slow
                    match try!(segment.load_term_directory(field_ref, term_ref)) {
                        Some(doc_id_set) => {
                            if doc_id_set.contains_doc(doc_id) {
                                // Read field length
                                // TODO: we only need this for BM25
                                let field_length_raw = try!(segment.load_stored_field_value_raw(doc_id, field_ref, b"len"));
                                let field_length = match field_length_raw {
                                    Some(value) => {
                                        let length_sqrt = (value[0] as f64) / 3.0 + 1.0;
                                        length_sqrt * length_sqrt
                                    }
                                    None => 1.0
                                };

                                // Read term frequency
                                let mut value_type = vec![b't', b'f'];
                                value_type.extend(term_ref.ord().to_string().as_bytes());
                                let term_frequency_raw = try!(segment.load_stored_field_value_raw(doc_id, field_ref, &value_type));
                                let term_frequency = match term_frequency_raw {
                                    Some(value) => BigEndian::read_i64(&value),
                                    None => 1,
                                };

                                let score = scorer.similarity_model.score(term_frequency as u32, field_length, try!(stats.total_tokens(field_ref)) as u64, try!(stats.total_docs(field_ref)) as u64, try!(stats.term_document_frequency(field_ref, term_ref)) as u64);
                                stack.push(score * scorer.boost);
                            } else {
                                stack.push(0.0f64);
                            }
                        }
                        None => stack.push(0.0f64),
                    }
                }
                ScoreFunctionOp::CombinatorScorer(num_vals, ref scorer) => {
                    let score = match *scorer {
                        CombinatorScorer::Avg => {
                            let mut total_score = 0.0f64;

                            for _ in 0..num_vals {
                                total_score += stack.pop().expect("document scorer: stack underflow");
                            }

                            total_score / num_vals as f64
                        }
                        CombinatorScorer::Max => {
                            let mut max_score = 0.0f64;

                            for _ in 0..num_vals {
                                let score = stack.pop().expect("document scorer: stack underflow");
                                if score > max_score {
                                    max_score = score
                                }
                            }

                            max_score
                        }
                    };

                    stack.push(score);
                }
            }
        }

        if !stack.len() == 1 {
            // This shouldn't be possible unless there's a bug in the planner
            panic!("document scorer: stack size too big ({})", stack.len());
        }

        Ok(stack.pop().expect("document scorer: stack underflow"))
    }

    fn search_segment<C: Collector, S: Segment, R: StatisticsReader>(&self, collector: &mut C, plan: &SearchPlan, segment: &S, mut stats: &mut R) -> Result<(), SegmentReadError> {
        let matches = try!(self.search_segment_boolean_phase(&plan.boolean_query, plan.boolean_query_is_negated, segment));

        // Score documents and pass to collector
        for doc in matches.iter() {
            let score = try!(self.score_doc(doc, &plan.score_function, segment, stats));

            let doc_ref = segment.doc_ref(doc);
            let doc_match = DocumentMatch::new_scored(doc_ref.as_u64(), score);
            collector.collect(doc_match);
        }

        Ok(())
    }

    pub fn search<C: Collector>(&self, collector: &mut C, query: &Query) -> Result<(), SegmentReadError> {
        // Plan query
        let plan = plan_query(&self, query, collector.needs_score());

        // Initialise statistics reader
        let mut stats = RocksDBStatisticsReader::new(&self);

        // Run query on each segment
        for segment in self.store.segments.iter_active(&self) {
            try!(self.search_segment(collector, &plan, &segment, &mut stats));
        }

        Ok(())
    }
}
<commit_msg>Moved generic search code out of RocksDBIndexReader<commit_after>mod statistics;
mod planner;

use kite::query::Query;
use kite::collectors::{Collector, DocumentMatch};
use byteorder::{ByteOrder, BigEndian};

use super::RocksDBIndexReader;
use doc_id_set::DocIdSet;
use segment::{Segment, SegmentReadError};
use search::statistics::{StatisticsReader, RocksDBStatisticsReader};
use search::planner::{SearchPlan, plan_query};
use search::planner::boolean_query::BooleanQueryOp;
use search::planner::score_function::{CombinatorScorer, ScoreFunctionOp};

fn search_segment_boolean_phase<S: Segment>(boolean_query: &Vec<BooleanQueryOp>, is_negated: bool, segment: &S) -> Result<DocIdSet, SegmentReadError> {
    // Execute boolean query
    let mut stack = Vec::new();
    for op in boolean_query.iter() {
        match *op {
            BooleanQueryOp::PushEmpty => {
                stack.push(DocIdSet::new_filled(0));
            }
            BooleanQueryOp::PushFull => {
                stack.push(DocIdSet::new_filled(65536));
            }
            BooleanQueryOp::PushTermDirectory(field_ref, term_ref) => {
                match try!(segment.load_term_directory(field_ref, term_ref)) {
                    Some(doc_id_set) => stack.push(doc_id_set),
                    None => stack.push(DocIdSet::new_filled(0)),
                }
            }
            BooleanQueryOp::PushDeletionList => {
                    match try!(segment.load_deletion_list()) {
                    Some(doc_id_set) => stack.push(doc_id_set),
                    None => stack.push(DocIdSet::new_filled(0)),
                }
            }
            BooleanQueryOp::And => {
                let b = stack.pop().expect("boolean query executor: stack underflow");
                let a = stack.pop().expect("boolean query executor: stack underflow");
                stack.push(a.intersection(&b));
            }
            BooleanQueryOp::Or => {
                let b = stack.pop().expect("boolean query executor: stack underflow");
                let a = stack.pop().expect("boolean query executor: stack underflow");
                stack.push(a.union(&b));
            }
            BooleanQueryOp::AndNot => {
                let b = stack.pop().expect("boolean query executor: stack underflow");
                let a = stack.pop().expect("boolean query executor: stack underflow");
                stack.push(a.exclusion(&b));
            }
        }
    }

    if !stack.len() == 1 {
        // This shouldn't be possible unless there's a bug in the planner
        panic!("boolean query executor: stack size too big ({})", stack.len());
    }
    let mut matches = stack.pop().unwrap();

    // Invert the list if the query is negated
    if is_negated {
        let total_docs = try!(segment.load_statistic(b"total_docs")).unwrap_or(0);
        let all_docs = DocIdSet::new_filled(total_docs as u32);
        matches = all_docs.exclusion(&matches);
    }

    Ok(matches)
}

fn score_doc<S: Segment, R: StatisticsReader>(doc_id: u16, score_function: &Vec<ScoreFunctionOp>, segment: &S, mut stats: &mut R) -> Result<f64, SegmentReadError> {
    // Execute score function
    let mut stack = Vec::new();
    for op in score_function.iter() {
        match *op {
            ScoreFunctionOp::Literal(val) => stack.push(val),
            ScoreFunctionOp::TermScorer(field_ref, term_ref, ref scorer) => {
                // TODO: Check this isn't really slow
                match try!(segment.load_term_directory(field_ref, term_ref)) {
                    Some(doc_id_set) => {
                        if doc_id_set.contains_doc(doc_id) {
                            // Read field length
                            // TODO: we only need this for BM25
                            let field_length_raw = try!(segment.load_stored_field_value_raw(doc_id, field_ref, b"len"));
                            let field_length = match field_length_raw {
                                Some(value) => {
                                    let length_sqrt = (value[0] as f64) / 3.0 + 1.0;
                                    length_sqrt * length_sqrt
                                }
                                None => 1.0
                            };

                            // Read term frequency
                            let mut value_type = vec![b't', b'f'];
                            value_type.extend(term_ref.ord().to_string().as_bytes());
                            let term_frequency_raw = try!(segment.load_stored_field_value_raw(doc_id, field_ref, &value_type));
                            let term_frequency = match term_frequency_raw {
                                Some(value) => BigEndian::read_i64(&value),
                                None => 1,
                            };

                            let score = scorer.similarity_model.score(term_frequency as u32, field_length, try!(stats.total_tokens(field_ref)) as u64, try!(stats.total_docs(field_ref)) as u64, try!(stats.term_document_frequency(field_ref, term_ref)) as u64);
                            stack.push(score * scorer.boost);
                        } else {
                            stack.push(0.0f64);
                        }
                    }
                    None => stack.push(0.0f64),
                }
            }
            ScoreFunctionOp::CombinatorScorer(num_vals, ref scorer) => {
                let score = match *scorer {
                    CombinatorScorer::Avg => {
                        let mut total_score = 0.0f64;

                        for _ in 0..num_vals {
                            total_score += stack.pop().expect("document scorer: stack underflow");
                        }

                        total_score / num_vals as f64
                    }
                    CombinatorScorer::Max => {
                        let mut max_score = 0.0f64;

                        for _ in 0..num_vals {
                            let score = stack.pop().expect("document scorer: stack underflow");
                            if score > max_score {
                                max_score = score
                            }
                        }

                        max_score
                    }
                };

                stack.push(score);
            }
        }
    }

    if !stack.len() == 1 {
        // This shouldn't be possible unless there's a bug in the planner
        panic!("document scorer: stack size too big ({})", stack.len());
    }

    Ok(stack.pop().expect("document scorer: stack underflow"))
}

fn search_segment<C: Collector, S: Segment, R: StatisticsReader>(collector: &mut C, plan: &SearchPlan, segment: &S, mut stats: &mut R) -> Result<(), SegmentReadError> {
    let matches = try!(search_segment_boolean_phase(&plan.boolean_query, plan.boolean_query_is_negated, segment));

    // Score documents and pass to collector
    for doc in matches.iter() {
        let score = try!(score_doc(doc, &plan.score_function, segment, stats));

        let doc_ref = segment.doc_ref(doc);
        let doc_match = DocumentMatch::new_scored(doc_ref.as_u64(), score);
        collector.collect(doc_match);
    }

    Ok(())
}

impl<'a> RocksDBIndexReader<'a> {
    pub fn search<C: Collector>(&self, collector: &mut C, query: &Query) -> Result<(), SegmentReadError> {
        // Plan query
        let plan = plan_query(&self, query, collector.needs_score());

        // Initialise statistics reader
        let mut stats = RocksDBStatisticsReader::new(&self);

        // Run query on each segment
        for segment in self.store.segments.iter_active(&self) {
            try!(search_segment(collector, &plan, &segment, &mut stats));
        }

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


<commit_before>use protocol::Protocol;
use transport::Transport;
use Result;

pub trait Processor<P: Protocol, T: Transport> {
    fn process(&mut self, prot: &mut P, transport: &mut T) -> Result<()>;
}

<commit_msg>Change Processor::process to receive &self<commit_after>use protocol::Protocol;
use transport::Transport;
use Result;

pub trait Processor<P: Protocol, T: Transport> {
    fn process(&self, prot: &mut P, transport: &mut T) -> Result<()>;
}

<|endoftext|>


<commit_before>#[link(name = "codegen")];

#[feature(macro_rules)];

use std::io;
use std::io::{File, Truncate, Write, fs};
use std::os;

pub mod branchify;
pub mod status;
pub mod read_method;

fn main() {
    let args = os::args();
    match args.len() {
        0 => {
            println("usage: codegen [read_method|status].rs <output-dir>");
            os::set_exit_status(1);
        },
        3 => {
            let output_dir = Path::new(args[2].as_slice());
            // TODO: maybe not 0777?
            if !output_dir.exists() {
                fs::mkdir(&output_dir, 0b111_111_111);
            }

            match args[1] {
                ~"read_method.rs" => read_method::generate(&output_dir),
                ~"status.rs" => status::generate(&output_dir),
                s => {
                    println!("unknown thing-to-generate '{}'", s);
                    os::set_exit_status(1);
                }
            }
        },
        _ => {
            println!("usage: {} [read_method|status].rs", args[0]);
            os::set_exit_status(1);
        }
    }
}

pub fn get_writer(output_dir: &Path, filename: &str) -> ~Writer {
    let mut output_file = output_dir.clone();
    output_file.push(filename);
    match io::result(|| File::open_mode(&output_file, Truncate, Write)) {
        Ok(writer) => ~writer as ~Writer,
        Err(e) => fail!("Unable to write file: {}", e.desc),
    }
}
<commit_msg>Update for mozilla/rust#10697.<commit_after>#[link(name = "codegen")];

#[feature(macro_rules)];

use std::io;
use std::io::{File, Truncate, Write, fs};
use std::os;

pub mod branchify;
pub mod status;
pub mod read_method;

fn main() {
    let args = os::args();
    match args.len() {
        0 => {
            println("usage: codegen [read_method|status].rs <output-dir>");
            os::set_exit_status(1);
        },
        3 => {
            let output_dir = Path::init(args[2].as_slice());
            // TODO: maybe not 0777?
            if !output_dir.exists() {
                fs::mkdir(&output_dir, 0b111_111_111);
            }

            match args[1] {
                ~"read_method.rs" => read_method::generate(&output_dir),
                ~"status.rs" => status::generate(&output_dir),
                s => {
                    println!("unknown thing-to-generate '{}'", s);
                    os::set_exit_status(1);
                }
            }
        },
        _ => {
            println!("usage: {} [read_method|status].rs", args[0]);
            os::set_exit_status(1);
        }
    }
}

pub fn get_writer(output_dir: &Path, filename: &str) -> ~Writer {
    let mut output_file = output_dir.clone();
    output_file.push(filename);
    match io::result(|| File::open_mode(&output_file, Truncate, Write)) {
        Ok(writer) => ~writer as ~Writer,
        Err(e) => fail!("Unable to write file: {}", e.desc),
    }
}
<|endoftext|>


<commit_before>//! Command line modules: has one function which takes input parameters from commandline
//! and parses them.
use super::{config, data};

static APP_TITLE: &str = concat!("The audiobook finder (", env!("CARGO_PKG_NAME"), ")");

static ARG_NET: &str = "net";
static ARG_TUI: &str = "tui";
static ARG_WEBUI: &str = "webui";
static ARG_KEEP_ALIVE: &str = "keep";
static ARG_BROWSER: &str = "browser";
static ARG_BROWSER_PORT: &str = "port";

static INPUT_FOLDERS: &str = "folders";

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");
const HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE");
const DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION");

/// Get all start values which are passed from command line
///
/// Get all start values and returns the following tuple
/// ui_paths,
/// has_tui,
/// has_webui,
/// has_net,
/// keep_alive,
/// open_browser,
/// web_port,
/// has_ui,
pub fn get_start_values() -> (Vec<String>, bool, bool, bool, bool, bool, u16, bool) {
    let parse_args = clap::App::new(APP_TITLE)
        .version(VERSION)
        .author(AUTHORS)
        .about(DESCRIPTION)
        .long_about::<&str>(
            &[
                &DESCRIPTION,
                "\n\
                 It reads data from possibly multiple given path(s). Via local network it searches \
                 for other instances of the program, and will later exchange data securely.\n\
                 All information gathered will be used to find duplicates, versions of \
                 different quality, different tags for same content (spelling, \
                 incompleteness).\n\
                 For documentation see: ",
                &HOMEPAGE,
                "\n \
                 A program to learn, embrace, and love Rust! \n\
                 Have fun!",
            ]
            .concat(),
        )
        .arg(
            clap::Arg::with_name("config")
                .short("c")
                .long("config")
                .value_name("FILE")
                .help("Sets custom config file (not implemented yet)")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name(ARG_TUI)
                .short("t")
                .long(ARG_TUI)
                .help("Run with TUI")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_WEBUI)
                .short("w")
                .long(ARG_WEBUI)
                .help("Run with-in a webui.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_BROWSER_PORT)
                .short("p")
                .long(ARG_BROWSER_PORT)
                .help(
                    &vec![
                        "Define port for webui (only works with webui).\nDefault port is:"
                            .to_string(),
                        config::net::WEB_PORT_DEFAULT.to_string(),
                        " but please choose from 8080 to 8099)".to_string(),
                    ]
                    .join(""),
                )
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name(ARG_NET)
                .short("n")
                .long(ARG_NET)
                .help("With net search for other audiobookfinders running in local network.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_KEEP_ALIVE)
                .short("k")
                .long(ARG_KEEP_ALIVE)
                .help("With keep alive process will continue even after search has been performed.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_BROWSER)
                .short("b")
                .long(ARG_BROWSER)
                .help("Shall browser be openend automatically (only works with webui).")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(INPUT_FOLDERS)
                .help(
                    &[
                        "Sets multiple input folder(s) to be searched for audio files. (Max ",
                        &config::data::PATHS_MAX.to_string(),
                        " input folders will be used!)",
                    ]
                    .concat(),
                )
                .multiple(true)
                .required(false),
        )
        .get_matches();
    // tricky thing, but I really like that
    let all_pathes = if let Some(correct_input) = parse_args.values_of(INPUT_FOLDERS) {
        correct_input.collect()
    } else {
        vec!["."]
    };

    //
    // check argments if tui and net search is needed
    //
    let has_arg = |x: &str| parse_args.is_present(x);

    let has_tui = has_arg(ARG_TUI);
    let has_webui = has_arg(ARG_WEBUI);
    let has_net = has_arg(ARG_NET);
    let has_port = has_arg(ARG_BROWSER_PORT);
    let keep_alive = has_arg(ARG_KEEP_ALIVE);
    let open_browser = has_arg(ARG_BROWSER);

    let web_port = {
        let web_default_string = config::net::WEB_PORT_DEFAULT.to_string();
        // when to write to console
        let has_to_write_console = !has_tui && has_webui;

        let parsed_value = parse_args
            .value_of(ARG_BROWSER_PORT)
            .unwrap_or_else(|| {
                if has_to_write_console && has_port {
                    println!(
                        "Port argument was bad, using default port {}!",
                        config::net::WEB_ADDR
                    );
                }
                &web_default_string
            })
            .parse::<u16>()
            .unwrap_or_else(|_| {
                if has_to_write_console && has_port {
                    println!(
                        "Invalid port input, using default port {}!",
                        &web_default_string
                    );
                }
                config::net::WEB_PORT_DEFAULT
            });
        if parsed_value < config::net::WEB_PORT_MIN || parsed_value > config::net::WEB_PORT_MAX {
            if has_to_write_console {
                let web_max_string = config::net::WEB_PORT_MAX.to_string();
                let web_min_string = config::net::WEB_PORT_MIN.to_string();
                println!(
                    "Port not in range {} .. {}, using default {}!",
                    &web_min_string, &web_max_string, &web_default_string
                );
            }
            config::net::WEB_PORT_DEFAULT
        } else {
            parsed_value
        }
    };

    // extended help for certain option combinations
    if !has_tui && has_webui && !open_browser {
        println!(
            "Open http://{}:{} to start!",
            config::net::WEB_ADDR,
            config::net::WEB_PORT_DEFAULT
        );
        println!("The webui needs to get the start signal from there");
    }

    // either one will have a ui, representing data and error messages
    let has_ui = has_tui || has_webui;

    // 1) convert to strings
    let unchecked_strings = all_pathes.iter().map(|s| s.to_string()).collect();
    // 2) clean-up
    let cleaned_paths = data::clean_paths(&unchecked_strings);
    // 3) and then cut-off unwanted parts (more paths input than PATHS_MAX)
    let ui_paths = cleaned_paths
        .into_iter()
        .enumerate()
        .filter(|(i, _el)| i < &config::data::PATHS_MAX)
        .map(|(_i, el)| el)
        .collect();
    (
        ui_paths,
        has_tui,
        has_webui,
        has_net,
        keep_alive,
        open_browser,
        web_port,
        has_ui,
    )
}
<commit_msg>[clean-up] added output message<commit_after>//! Command line modules: has one function which takes input parameters from commandline
//! and parses them.
use super::{config, data};

static APP_TITLE: &str = concat!("The audiobook finder (", env!("CARGO_PKG_NAME"), ")");

static ARG_NET: &str = "net";
static ARG_TUI: &str = "tui";
static ARG_WEBUI: &str = "webui";
static ARG_KEEP_ALIVE: &str = "keep";
static ARG_BROWSER: &str = "browser";
static ARG_BROWSER_PORT: &str = "port";

static INPUT_FOLDERS: &str = "folders";

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");
const HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE");
const DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION");

/// Get all start values which are passed from command line
///
/// Get all start values and returns the following tuple
/// ui_paths,
/// has_tui,
/// has_webui,
/// has_net,
/// keep_alive,
/// open_browser,
/// web_port,
/// has_ui,
pub fn get_start_values() -> (Vec<String>, bool, bool, bool, bool, bool, u16, bool) {
    let parse_args = clap::App::new(APP_TITLE)
        .version(VERSION)
        .author(AUTHORS)
        .about(DESCRIPTION)
        .long_about::<&str>(
            &[
                &DESCRIPTION,
                "\n\
                 It reads data from possibly multiple given path(s). Via local network it searches \
                 for other instances of the program, and will later exchange data securely.\n\
                 All information gathered will be used to find duplicates, versions of \
                 different quality, different tags for same content (spelling, \
                 incompleteness).\n\
                 For documentation see: ",
                &HOMEPAGE,
                "\n \
                 A program to learn, embrace, and love Rust! \n\
                 Have fun!",
            ]
            .concat(),
        )
        .arg(
            clap::Arg::with_name("config")
                .short("c")
                .long("config")
                .value_name("FILE")
                .help("Sets custom config file (not implemented yet)")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name(ARG_TUI)
                .short("t")
                .long(ARG_TUI)
                .help("Run with TUI")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_WEBUI)
                .short("w")
                .long(ARG_WEBUI)
                .help("Run with-in a webui.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_BROWSER_PORT)
                .short("p")
                .long(ARG_BROWSER_PORT)
                .help(
                    &vec![
                        "Define port for webui (only works with webui).\nDefault port is:"
                            .to_string(),
                        config::net::WEB_PORT_DEFAULT.to_string(),
                        " but please choose from 8080 to 8099)".to_string(),
                    ]
                    .join(""),
                )
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name(ARG_NET)
                .short("n")
                .long(ARG_NET)
                .help("With net search for other audiobookfinders running in local network.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_KEEP_ALIVE)
                .short("k")
                .long(ARG_KEEP_ALIVE)
                .help("With keep alive process will continue even after search has been performed.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_BROWSER)
                .short("b")
                .long(ARG_BROWSER)
                .help("Shall browser be openend automatically (only works with webui).")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(INPUT_FOLDERS)
                .help(
                    &[
                        "Sets multiple input folder(s) to be searched for audio files. (Max ",
                        &config::data::PATHS_MAX.to_string(),
                        " input folders will be used!)",
                    ]
                    .concat(),
                )
                .multiple(true)
                .required(false),
        )
        .get_matches();
    // tricky thing, but I really like that
    let all_pathes = if let Some(correct_input) = parse_args.values_of(INPUT_FOLDERS) {
        correct_input.collect()
    } else {
        vec!["."]
    };

    //
    // check argments if tui and net search is needed
    //
    let has_arg = |x: &str| parse_args.is_present(x);

    let has_tui = has_arg(ARG_TUI);
    let has_webui = has_arg(ARG_WEBUI);
    let has_net = has_arg(ARG_NET);
    let has_port = has_arg(ARG_BROWSER_PORT);
    let keep_alive = has_arg(ARG_KEEP_ALIVE);
    let open_browser = has_arg(ARG_BROWSER);

    let web_port = {
        let web_default_string = config::net::WEB_PORT_DEFAULT.to_string();
        // when to write to console
        let has_to_write_console = !has_tui && has_webui;

        let parsed_value = parse_args
            .value_of(ARG_BROWSER_PORT)
            .unwrap_or_else(|| {
                if has_to_write_console && has_port {
                    println!(
                        "Port argument was bad, using default port {}!",
                        config::net::WEB_ADDR
                    );
                }
                &web_default_string
            })
            .parse::<u16>()
            .unwrap_or_else(|_| {
                if has_to_write_console && has_port {
                    println!(
                        "Invalid port input, using default port {}!",
                        &web_default_string
                    );
                }
                config::net::WEB_PORT_DEFAULT
            });
        if parsed_value < config::net::WEB_PORT_MIN || parsed_value > config::net::WEB_PORT_MAX {
            if has_to_write_console {
                let web_max_string = config::net::WEB_PORT_MAX.to_string();
                let web_min_string = config::net::WEB_PORT_MIN.to_string();
                println!(
                    "Port not in range {} .. {}, using default {}!",
                    &web_min_string, &web_max_string, &web_default_string
                );
            }
            config::net::WEB_PORT_DEFAULT
        } else {
            parsed_value
        }
    };

    // extended help for certain option combinations
    if !has_tui && has_webui && !open_browser {
        println!(
            "Open http://{}:{} to start!",
            config::net::WEB_ADDR,
            config::net::WEB_PORT_DEFAULT
        );
        println!("The webui needs to get the start signal from there");
    }

    // either one will have a ui, representing data and error messages
    let has_ui = has_tui || has_webui;

    // 1) convert to strings
    let unchecked_strings = all_pathes.iter().map(|s| s.to_string()).collect();
    // 2) clean-up
    let cleaned_paths = data::clean_paths(&unchecked_strings);
    if cleaned_paths.len() != unchecked_strings.len() && !has_tui && !has_webui && !open_browser {
        println!("Some paths/folders intersect and will not be used!");
    }
    // 3) and then cut-off unwanted parts (more paths input than PATHS_MAX)
    let ui_paths = cleaned_paths
        .into_iter()
        .enumerate()
        .filter(|(i, _el)| i < &config::data::PATHS_MAX)
        .map(|(_i, el)| el)
        .collect();
    (
        ui_paths,
        has_tui,
        has_webui,
        has_net,
        keep_alive,
        open_browser,
        web_port,
        has_ui,
    )
}
<|endoftext|>


<commit_before>// crypto_generichash_blake2b.h

pub const crypto_generichash_blake2b_BYTES_MIN: usize = 16;
pub const crypto_generichash_blake2b_BYTES_MAX: usize = 64;
pub const crypto_generichash_blake2b_BYTES: usize = 32;
pub const crypto_generichash_blake2b_KEYBYTES_MIN: usize = 16;
pub const crypto_generichash_blake2b_KEYBYTES_MAX: usize = 64;
pub const crypto_generichash_blake2b_KEYBYTES: usize = 32;
pub const crypto_generichash_blake2b_SALTBYTES: usize = 16;
pub const crypto_generichash_blake2b_PERSONALBYTES: usize = 16;

extern {
    pub fn crypto_generichash_blake2b_bytes_min() -> size_t;
    pub fn crypto_generichash_blake2b_bytes_max() -> size_t;
    pub fn crypto_generichash_blake2b_bytes() -> size_t;
    pub fn crypto_generichash_blake2b_keybytes_min() -> size_t;
    pub fn crypto_generichash_blake2b_keybytes_max() -> size_t;
    pub fn crypto_generichash_blake2b_keybytes() -> size_t;
    pub fn crypto_generichash_blake2b_saltbytes() -> size_t;
    pub fn crypto_generichash_blake2b_personalbytes() -> size_t;

    pub fn crypto_generichash_blake2b(
        out: *mut u8,
        outlen: size_t,
        in_: *const u8,
        inlen: c_ulonglong,
        key: *const u8,
        keylen: size_t)
        -> c_int;

    pub fn crypto_generichash_blake2b_salt_personal(
        out: *mut u8,
        outlen: size_t,
        in_: *const u8,
        inlen: c_ulonglong,
        key: *const u8,
        keylen: size_t,
        salt: *const [u8; crypto_generichash_blake2b_SALTBYTES],
        personal: *const [u8; crypto_generichash_blake2b_PERSONALBYTES])
        -> c_int;
}

#[test]
fn test_crypto_generichash_blake2b_bytes_min() {
    assert_eq!(unsafe { crypto_generichash_blake2b_bytes_min() as usize },
                        crypto_generichash_blake2b_BYTES_MIN)
}

#[test]
fn test_crypto_generichash_blake2b_bytes_max() {
    assert_eq!(unsafe { crypto_generichash_blake2b_bytes_max() as usize },
                        crypto_generichash_blake2b_BYTES_MAX)
}

#[test]
fn test_crypto_generichash_blake2b_bytes() {
    assert_eq!(unsafe { crypto_generichash_blake2b_bytes() as usize },
                        crypto_generichash_blake2b_BYTES)
}

#[test]
fn test_crypto_generichash_blake2b_keybytes_min() {
    assert_eq!(unsafe { crypto_generichash_blake2b_keybytes_min() as usize },
                        crypto_generichash_blake2b_KEYBYTES_MIN)
}

#[test]
fn test_crypto_generichash_blake2b_keybytes_max() {
    assert_eq!(unsafe { crypto_generichash_blake2b_keybytes_max() as usize },
                        crypto_generichash_blake2b_KEYBYTES_MAX)
}

#[test]
fn test_crypto_generichash_blake2b_keybytes() {
    assert_eq!(unsafe { crypto_generichash_blake2b_keybytes() as usize },
                        crypto_generichash_blake2b_KEYBYTES)
}

#[test]
fn test_crypto_generichash_blake2b_saltbytes() {
    assert_eq!(unsafe { crypto_generichash_blake2b_saltbytes() as usize },
                        crypto_generichash_blake2b_SALTBYTES)
}

#[test]
fn test_crypto_generichash_blake2b_personalbytes() {
    assert_eq!(unsafe { crypto_generichash_blake2b_personalbytes() as usize },
                        crypto_generichash_blake2b_PERSONALBYTES)
}
<commit_msg>Whitespace changes<commit_after>// crypto_generichash_blake2b.h

pub const crypto_generichash_blake2b_BYTES_MIN: usize = 16;
pub const crypto_generichash_blake2b_BYTES_MAX: usize = 64;
pub const crypto_generichash_blake2b_BYTES: usize = 32;
pub const crypto_generichash_blake2b_KEYBYTES_MIN: usize = 16;
pub const crypto_generichash_blake2b_KEYBYTES_MAX: usize = 64;
pub const crypto_generichash_blake2b_KEYBYTES: usize = 32;
pub const crypto_generichash_blake2b_SALTBYTES: usize = 16;
pub const crypto_generichash_blake2b_PERSONALBYTES: usize = 16;

extern {
    pub fn crypto_generichash_blake2b_bytes_min() -> size_t;
    pub fn crypto_generichash_blake2b_bytes_max() -> size_t;
    pub fn crypto_generichash_blake2b_bytes() -> size_t;
    pub fn crypto_generichash_blake2b_keybytes_min() -> size_t;
    pub fn crypto_generichash_blake2b_keybytes_max() -> size_t;
    pub fn crypto_generichash_blake2b_keybytes() -> size_t;
    pub fn crypto_generichash_blake2b_saltbytes() -> size_t;
    pub fn crypto_generichash_blake2b_personalbytes() -> size_t;

    pub fn crypto_generichash_blake2b(
        out: *mut u8,
        outlen: size_t,
        in_: *const u8,
        inlen: c_ulonglong,
        key: *const u8,
        keylen: size_t)
        -> c_int;

    pub fn crypto_generichash_blake2b_salt_personal(
        out: *mut u8,
        outlen: size_t,
        in_: *const u8,
        inlen: c_ulonglong,
        key: *const u8,
        keylen: size_t,
        salt: *const [u8; crypto_generichash_blake2b_SALTBYTES],
        personal: *const [u8; crypto_generichash_blake2b_PERSONALBYTES])
        -> c_int;
}

#[test]
fn test_crypto_generichash_blake2b_bytes_min() {
    assert_eq!(unsafe { crypto_generichash_blake2b_bytes_min() as usize },
                        crypto_generichash_blake2b_BYTES_MIN)
}

#[test]
fn test_crypto_generichash_blake2b_bytes_max() {
    assert_eq!(unsafe { crypto_generichash_blake2b_bytes_max() as usize },
                        crypto_generichash_blake2b_BYTES_MAX)
}

#[test]
fn test_crypto_generichash_blake2b_bytes() {
    assert_eq!(unsafe { crypto_generichash_blake2b_bytes() as usize },
                        crypto_generichash_blake2b_BYTES)
}

#[test]
fn test_crypto_generichash_blake2b_keybytes_min() {
    assert_eq!(unsafe { crypto_generichash_blake2b_keybytes_min() as usize },
                        crypto_generichash_blake2b_KEYBYTES_MIN)
}

#[test]
fn test_crypto_generichash_blake2b_keybytes_max() {
    assert_eq!(unsafe { crypto_generichash_blake2b_keybytes_max() as usize },
                        crypto_generichash_blake2b_KEYBYTES_MAX)
}

#[test]
fn test_crypto_generichash_blake2b_keybytes() {
    assert_eq!(unsafe { crypto_generichash_blake2b_keybytes() as usize },
                        crypto_generichash_blake2b_KEYBYTES)
}

#[test]
fn test_crypto_generichash_blake2b_saltbytes() {
    assert_eq!(unsafe { crypto_generichash_blake2b_saltbytes() as usize },
                        crypto_generichash_blake2b_SALTBYTES)
}

#[test]
fn test_crypto_generichash_blake2b_personalbytes() {
    assert_eq!(unsafe { crypto_generichash_blake2b_personalbytes() as usize },
                        crypto_generichash_blake2b_PERSONALBYTES)
}
<|endoftext|>


<commit_before>//! EPMD client and other EPMD related components.
//!
//! "EPMD" stands for "Erlang Port Mapper Daemon" and
//! it provides name resolution functionalities for distributed erlang nodes.
//!
//! See [EPMD Protocol (Erlang Official Doc)](https://www.erlang.org/doc/apps/erts/erl_dist_protocol.html#epmd-protocol)
//! for more details about the protocol.
use crate::socket::Socket;
use crate::Creation;
use futures::io::{AsyncRead, AsyncWrite};
use std::str::FromStr;

/// Default EPMD listening port.
pub const DEFAULT_EPMD_PORT: u16 = 4369;

const TAG_DUMP_REQ: u8 = 100;
const TAG_KILL_REQ: u8 = 107;
const TAG_NAMES_REQ: u8 = 110;
const TAG_ALIVE2_X_RESP: u8 = 118;
const TAG_PORT2_RESP: u8 = 119;
const TAG_ALIVE2_REQ: u8 = 120;
const TAG_ALIVE2_RESP: u8 = 121;
const TAG_PORT_PLEASE2_REQ: u8 = 122;

/// Node name and port.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NodeNameAndPort {
    /// Node name.
    pub name: String,

    /// Listening port of this node.
    pub port: u16,
}

impl FromStr for NodeNameAndPort {
    type Err = EpmdError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let error = || EpmdError::MalformedNodeNameAndPortLine { line: s.to_owned() };

        if !s.starts_with("name ") {
            return Err(error());
        }

        let s = &s["name ".len()..];
        let pos = s.find(" at port ").ok_or_else(error)?;
        let name = s[..pos].to_string();
        let port = s[pos + " at port ".len()..].parse().map_err(|_| error())?;
        Ok(Self { name, port })
    }
}

/// Type of a distributed node.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum NodeType {
    /// Hidden node (C-node).
    Hidden = 72,

    /// Normal Erlang node.
    Normal = 77,
}

impl TryFrom<u8> for NodeType {
    type Error = EpmdError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            72 => Ok(Self::Hidden),
            77 => Ok(Self::Normal),
            _ => Err(EpmdError::UnknownNodeType { value }),
        }
    }
}

/// Protocol for communicating with a distributed node.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Protocol {
    /// TCP/IPv4.
    TcpIpV4 = 0,
}

impl TryFrom<u8> for Protocol {
    type Error = EpmdError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::TcpIpV4),
            _ => Err(EpmdError::UnknownProtocol { value }),
        }
    }
}

/// Node information.
#[derive(Debug, Clone)]
pub struct NodeInfo {
    /// The node name.
    pub name: String,

    /// The port number on which the node accept connection requests.
    pub port: u16,

    /// The node type.
    pub node_type: NodeType,

    /// The protocol for communicating with the node.
    pub protocol: Protocol,

    /// The highest distribution version that this node can handle.
    ///
    /// The value in Erlang/OTP R6B and later is 5.
    pub highest_version: u16,

    /// The lowest distribution version that this node can handle.
    ///
    /// The value in Erlang/OTP R6B and later is 5.
    pub lowest_version: u16,

    /// Extra field.
    pub extra: Vec<u8>,
}

impl NodeInfo {
    fn bytes_len(&self) -> usize {
        2 + self.name.len() + // name
        2 + // port
        1 + // node_type
        1 + // protocol
        2 + // highest_version
        2 + // lowest_version
        2 + self.extra.len() // extra
    }
}

/// EPMD related errors.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum EpmdError {
    /// Unknown response tag.
    #[error("received an unknown tag {tag} as the response of {request}")]
    UnknownResponseTag { request: &'static str, tag: u8 },

    /// Unknown node type.
    #[error("unknown node type {value}")]
    UnknownNodeType { value: u8 },

    /// Unknown protocol.
    #[error("unknown protocol {value}")]
    UnknownProtocol { value: u8 },

    /// Too long request.
    #[error("request byte size must be less than 0xFFFF, but got {size} bytes")]
    TooLongRequest { size: usize },

    /// PORT_PLEASE2_REQ request failure.
    #[error("EPMD responded an error code {code} against a PORT_PLEASE2_REQ request")]
    GetNodeInfoError { code: u8 },

    /// ALIVE2_REQ request failure.
    #[error("EPMD responded an error code {code} against an ALIVE2_REQ request")]
    RegisterNodeError { code: u8 },

    /// Malformed NAMES_RESP line.
    #[error("found a malformed NAMES_RESP line: expected_format=\"name {{NAME}} at port {{PORT}}\", actual_line={line:}")]
    MalformedNodeNameAndPortLine { line: String },

    /// I/O error.
    #[error(transparent)]
    Io(#[from] std::io::Error),
}

/// EPMD client.
#[derive(Debug)]
pub struct EpmdClient<T> {
    socket: Socket<T>,
}

impl<T> EpmdClient<T>
where
    T: AsyncRead + AsyncWrite + Unpin,
{
    /// Makes a new [`EpmdClient`] instance.
    ///
    /// `socket` is a connection to communicate with the target EPMD server.
    pub fn new(socket: T) -> Self {
        Self {
            socket: Socket::new(socket),
        }
    }

    /// Registers a node in EPMD.
    ///
    /// The connection created to the EPMD must be kept as long as the node is a distributed node.
    /// When the connection is closed, the node is automatically unregistered from the EPMD.
    pub async fn register(mut self, node: NodeInfo) -> Result<(T, Creation), EpmdError> {
        // Request.
        let size = 1 + node.bytes_len();
        let size = u16::try_from(size).map_err(|_| EpmdError::TooLongRequest { size })?;
        self.socket.write_u16(size).await?;
        self.socket.write_u8(TAG_ALIVE2_REQ).await?;
        self.socket.write_u16(node.port).await?;
        self.socket.write_u8(node.node_type as u8).await?;
        self.socket.write_u8(node.protocol as u8).await?;
        self.socket.write_u16(node.highest_version).await?;
        self.socket.write_u16(node.lowest_version).await?;
        self.socket.write_u16(node.name.len() as u16).await?;
        self.socket.write_all(node.name.as_bytes()).await?;
        self.socket.write_u16(node.extra.len() as u16).await?;
        self.socket.write_all(&node.extra).await?;
        self.socket.flush().await?;

        // Response.
        match self.socket.read_u8().await? {
            TAG_ALIVE2_RESP => {
                match self.socket.read_u8().await? {
                    0 => {}
                    code => return Err(EpmdError::RegisterNodeError { code }),
                }

                let creation = Creation::new(u32::from(self.socket.read_u16().await?));
                Ok((self.socket.into_inner(), creation))
            }
            TAG_ALIVE2_X_RESP => {
                match self.socket.read_u8().await? {
                    0 => {}
                    code => return Err(EpmdError::RegisterNodeError { code }),
                }

                let creation = Creation::new(self.socket.read_u32().await?);
                Ok((self.socket.into_inner(), creation))
            }
            tag => Err(EpmdError::UnknownResponseTag {
                request: "ALIVE2_REQ",
                tag,
            }),
        }
    }

    /// Gets all registered names from EPMD.
    pub async fn get_names(mut self) -> Result<Vec<NodeNameAndPort>, EpmdError> {
        // Request.
        self.socket.write_u16(1).await?; // Length
        self.socket.write_u8(TAG_NAMES_REQ).await?;
        self.socket.flush().await?;

        // Response.
        let _epmd_port = self.socket.read_u32().await?;
        let node_info_text = self.socket.read_string().await?;

        node_info_text
            .split('\n')
            .filter(|s| !s.is_empty())
            .map(NodeNameAndPort::from_str)
            .collect()
    }

    /// Gets the distribution port (and other information) of
    /// the `node_name` node from EPMD.
    ///
    /// If the node has not been registered in the connected EPMD, this method will return `None`.
    pub async fn get_node_info(mut self, node_name: &str) -> Result<Option<NodeInfo>, EpmdError> {
        // Request.
        let size = 1 + node_name.len();
        let size = u16::try_from(size).map_err(|_| EpmdError::TooLongRequest { size })?;
        self.socket.write_u16(size).await?;
        self.socket.write_u8(TAG_PORT_PLEASE2_REQ).await?;
        self.socket.write_all(node_name.as_bytes()).await?;
        self.socket.flush().await?;

        // Response.
        let tag = self.socket.read_u8().await?;
        if tag != TAG_PORT2_RESP {
            return Err(EpmdError::UnknownResponseTag {
                request: "NAMES_REQ",
                tag,
            });
        }

        match self.socket.read_u8().await? {
            0 => {}
            1 => {
                return Ok(None);
            }
            code => {
                return Err(EpmdError::GetNodeInfoError { code });
            }
        }

        Ok(Some(NodeInfo {
            port: self.socket.read_u16().await?,
            node_type: NodeType::try_from(self.socket.read_u8().await?)?,
            protocol: Protocol::try_from(self.socket.read_u8().await?)?,
            highest_version: self.socket.read_u16().await?,
            lowest_version: self.socket.read_u16().await?,
            name: self.socket.read_u16_string().await?,
            extra: self.socket.read_u16_bytes().await?,
        }))
    }

    /// Kills EPMD.
    ///
    /// This request kills the running EPMD.
    /// It is almost never used.
    ///
    /// If EPMD is killed, this method returns `"OK"`.
    pub async fn kill(mut self) -> Result<String, EpmdError> {
        // Request.
        self.socket.write_u16(1).await?;
        self.socket.write_u8(TAG_KILL_REQ).await?;
        self.socket.flush().await?;

        // Response.
        let result = self.socket.read_string().await?;
        Ok(result)
    }

    /// Dumps all data from EPMD.
    ///
    /// This request is not really used, it is to be regarded as a debug feature.
    ///
    /// The result value is a string written for each node kept in the connected EPMD.
    ///
    /// The format of each entry is
    ///
    /// ```shell
    /// "active name ${NODE_NAME} at port ${PORT}, fd = ${FD}\n"
    /// ```
    ///
    /// or
    ///
    /// ```shell
    /// "old/unused name ${NODE_NAME} at port ${PORT}, fd = ${FD}\n"
    /// ```
    pub async fn dump(mut self) -> Result<String, EpmdError> {
        // Request.
        self.socket.write_u16(1).await?;
        self.socket.write_u8(TAG_DUMP_REQ).await?;
        self.socket.flush().await?;

        // Response.
        let _epmd_port = self.socket.read_u32().await?;
        let info = self.socket.read_string().await?;
        Ok(info)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::process::{Child, Command};

    #[derive(Debug)]
    struct TestErlangNode {
        child: Child,
    }

    impl TestErlangNode {
        fn new(name: &str) -> std::io::Result<Self> {
            let child = Command::new("erl")
                .args(&["-sname", name, "-noshell"])
                .spawn()?;
            std::thread::sleep(std::time::Duration::from_secs(1));
            Ok(Self { child })
        }
    }

    impl Drop for TestErlangNode {
        fn drop(&mut self) {
            let _ = self.child.kill();
        }
    }

    async fn epmd_client() -> EpmdClient<smol::net::TcpStream> {
        let stream = smol::net::TcpStream::connect(("127.0.0.1", DEFAULT_EPMD_PORT))
            .await
            .unwrap();
        EpmdClient::new(stream)
    }

    #[test]
    fn epmd_client_works() {
        let node_name = "erl_dist_test";
        let erl_node = TestErlangNode::new(node_name).expect("failed to run a test erlang node");
        smol::block_on(async {
            // Get the information of an existing Erlang node.
            let info = epmd_client()
                .await
                .get_node_info(node_name)
                .await
                .expect("failed to get node info");
            let info = info.expect("no such node");
            assert_eq!(info.name, node_name);

            // Register a new node.
            let client = epmd_client().await;
            let new_node_name = "erl_dist_test_new_node";
            let new_node = NodeInfo {
                name: new_node_name.to_owned(),
                port: 3000,
                node_type: NodeType::Hidden,
                protocol: Protocol::TcpIpV4,
                highest_version: 6,
                lowest_version: 5,
                extra: Vec::new(),
            };
            let (stream, _creation) = client
                .register(new_node)
                .await
                .expect("failed to register a new node");

            // Get the information of the newly added Erlang node.
            let info = epmd_client()
                .await
                .get_node_info(new_node_name)
                .await
                .expect("failed to get node info");
            let info = info.expect("no such node");
            assert_eq!(info.name, new_node_name);

            // Deregister the node.
            std::mem::drop(stream);
            let info = epmd_client()
                .await
                .get_node_info(new_node_name)
                .await
                .expect("failed to get node info");
            assert!(info.is_none());
        });
        std::mem::drop(erl_node);
    }
}
<commit_msg>Add `HandshakeProtocolversion`<commit_after>//! EPMD client and other EPMD related components.
//!
//! "EPMD" stands for "Erlang Port Mapper Daemon" and
//! it provides name resolution functionalities for distributed erlang nodes.
//!
//! See [EPMD Protocol (Erlang Official Doc)](https://www.erlang.org/doc/apps/erts/erl_dist_protocol.html#epmd-protocol)
//! for more details about the protocol.
use crate::socket::Socket;
use crate::Creation;
use futures::io::{AsyncRead, AsyncWrite};
use std::str::FromStr;

/// Default EPMD listening port.
pub const DEFAULT_EPMD_PORT: u16 = 4369;

const TAG_DUMP_REQ: u8 = 100;
const TAG_KILL_REQ: u8 = 107;
const TAG_NAMES_REQ: u8 = 110;
const TAG_ALIVE2_X_RESP: u8 = 118;
const TAG_PORT2_RESP: u8 = 119;
const TAG_ALIVE2_REQ: u8 = 120;
const TAG_ALIVE2_RESP: u8 = 121;
const TAG_PORT_PLEASE2_REQ: u8 = 122;

/// Node name and port.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct NodeNameAndPort {
    /// Node name.
    pub name: String,

    /// Listening port of this node.
    pub port: u16,
}

impl FromStr for NodeNameAndPort {
    type Err = EpmdError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let error = || EpmdError::MalformedNodeNameAndPortLine { line: s.to_owned() };

        if !s.starts_with("name ") {
            return Err(error());
        }

        let s = &s["name ".len()..];
        let pos = s.find(" at port ").ok_or_else(error)?;
        let name = s[..pos].to_string();
        let port = s[pos + " at port ".len()..].parse().map_err(|_| error())?;
        Ok(Self { name, port })
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum HandshakeProtocolVersion {
    V5 = 5,
    V6 = 6,
}

impl std::fmt::Display for HandshakeProtocolVersion {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", *self as u16)
    }
}

impl TryFrom<u16> for HandshakeProtocolVersion {
    type Error = EpmdError;

    fn try_from(value: u16) -> Result<Self, Self::Error> {
        match value {
            5 => Ok(Self::V5),
            6 => Ok(Self::V6),
            _ => Err(EpmdError::UnknownVersion { value }),
        }
    }
}

/// Type of a distributed node.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum NodeType {
    /// Hidden node (C-node).
    Hidden = 72,

    /// Normal Erlang node.
    Normal = 77,
}

impl TryFrom<u8> for NodeType {
    type Error = EpmdError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            72 => Ok(Self::Hidden),
            77 => Ok(Self::Normal),
            _ => Err(EpmdError::UnknownNodeType { value }),
        }
    }
}

// TODO: rename
/// Protocol for communicating with a distributed node.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Protocol {
    /// TCP/IPv4.
    TcpIpV4 = 0,
}

impl TryFrom<u8> for Protocol {
    type Error = EpmdError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::TcpIpV4),
            _ => Err(EpmdError::UnknownProtocol { value }),
        }
    }
}

/// Node information.
#[derive(Debug, Clone)]
pub struct NodeInfo {
    /// The node name.
    pub name: String,

    /// The port number on which the node accept connection requests.
    pub port: u16,

    /// The node type.
    pub node_type: NodeType,

    /// The protocol for communicating with the node.
    pub protocol: Protocol,

    pub highest_version: HandshakeProtocolVersion,
    pub lowest_version: HandshakeProtocolVersion,

    /// Extra field.
    pub extra: Vec<u8>,
}

impl NodeInfo {
    fn bytes_len(&self) -> usize {
        2 + self.name.len() + // name
        2 + // port
        1 + // node_type
        1 + // protocol
        2 + // highest_version
        2 + // lowest_version
        2 + self.extra.len() // extra
    }
}

/// EPMD related errors.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum EpmdError {
    /// Unknown response tag.
    #[error("received an unknown tag {tag} as the response of {request}")]
    UnknownResponseTag { request: &'static str, tag: u8 },

    /// Unknown node type.
    #[error("unknown node type {value}")]
    UnknownNodeType { value: u8 },

    /// Unknown protocol.
    #[error("unknown protocol {value}")]
    UnknownProtocol { value: u8 },

    /// Unknown distribution protocol version.
    #[error("unknown distribution protocol version {value}")]
    UnknownVersion { value: u16 },

    /// Too long request.
    #[error("request byte size must be less than 0xFFFF, but got {size} bytes")]
    TooLongRequest { size: usize },

    /// PORT_PLEASE2_REQ request failure.
    #[error("EPMD responded an error code {code} against a PORT_PLEASE2_REQ request")]
    GetNodeInfoError { code: u8 },

    /// ALIVE2_REQ request failure.
    #[error("EPMD responded an error code {code} against an ALIVE2_REQ request")]
    RegisterNodeError { code: u8 },

    /// Malformed NAMES_RESP line.
    #[error("found a malformed NAMES_RESP line: expected_format=\"name {{NAME}} at port {{PORT}}\", actual_line={line:?}")]
    MalformedNodeNameAndPortLine { line: String },

    /// I/O error.
    #[error(transparent)]
    Io(#[from] std::io::Error),
}

/// EPMD client.
#[derive(Debug)]
pub struct EpmdClient<T> {
    socket: Socket<T>,
}

impl<T> EpmdClient<T>
where
    T: AsyncRead + AsyncWrite + Unpin,
{
    /// Makes a new [`EpmdClient`] instance.
    ///
    /// `socket` is a connection to communicate with the target EPMD server.
    pub fn new(socket: T) -> Self {
        Self {
            socket: Socket::new(socket),
        }
    }

    /// Registers a node in EPMD.
    ///
    /// The connection created to the EPMD must be kept as long as the node is a distributed node.
    /// When the connection is closed, the node is automatically unregistered from the EPMD.
    pub async fn register(mut self, node: NodeInfo) -> Result<(T, Creation), EpmdError> {
        // Request.
        let size = 1 + node.bytes_len();
        let size = u16::try_from(size).map_err(|_| EpmdError::TooLongRequest { size })?;
        self.socket.write_u16(size).await?;
        self.socket.write_u8(TAG_ALIVE2_REQ).await?;
        self.socket.write_u16(node.port).await?;
        self.socket.write_u8(node.node_type as u8).await?;
        self.socket.write_u8(node.protocol as u8).await?;
        self.socket.write_u16(node.highest_version as u16).await?;
        self.socket.write_u16(node.lowest_version as u16).await?;
        self.socket.write_u16(node.name.len() as u16).await?;
        self.socket.write_all(node.name.as_bytes()).await?;
        self.socket.write_u16(node.extra.len() as u16).await?;
        self.socket.write_all(&node.extra).await?;
        self.socket.flush().await?;

        // Response.
        match self.socket.read_u8().await? {
            TAG_ALIVE2_RESP => {
                match self.socket.read_u8().await? {
                    0 => {}
                    code => return Err(EpmdError::RegisterNodeError { code }),
                }

                let creation = Creation::new(u32::from(self.socket.read_u16().await?));
                Ok((self.socket.into_inner(), creation))
            }
            TAG_ALIVE2_X_RESP => {
                match self.socket.read_u8().await? {
                    0 => {}
                    code => return Err(EpmdError::RegisterNodeError { code }),
                }

                let creation = Creation::new(self.socket.read_u32().await?);
                Ok((self.socket.into_inner(), creation))
            }
            tag => Err(EpmdError::UnknownResponseTag {
                request: "ALIVE2_REQ",
                tag,
            }),
        }
    }

    /// Gets all registered names from EPMD.
    pub async fn get_names(mut self) -> Result<Vec<NodeNameAndPort>, EpmdError> {
        // Request.
        self.socket.write_u16(1).await?; // Length
        self.socket.write_u8(TAG_NAMES_REQ).await?;
        self.socket.flush().await?;

        // Response.
        let _epmd_port = self.socket.read_u32().await?;
        let node_info_text = self.socket.read_string().await?;

        node_info_text
            .split('\n')
            .filter(|s| !s.is_empty())
            .map(NodeNameAndPort::from_str)
            .collect()
    }

    /// Gets the distribution port (and other information) of
    /// the `node_name` node from EPMD.
    ///
    /// If the node has not been registered in the connected EPMD, this method will return `None`.
    pub async fn get_node_info(mut self, node_name: &str) -> Result<Option<NodeInfo>, EpmdError> {
        // Request.
        let size = 1 + node_name.len();
        let size = u16::try_from(size).map_err(|_| EpmdError::TooLongRequest { size })?;
        self.socket.write_u16(size).await?;
        self.socket.write_u8(TAG_PORT_PLEASE2_REQ).await?;
        self.socket.write_all(node_name.as_bytes()).await?;
        self.socket.flush().await?;

        // Response.
        let tag = self.socket.read_u8().await?;
        if tag != TAG_PORT2_RESP {
            return Err(EpmdError::UnknownResponseTag {
                request: "NAMES_REQ",
                tag,
            });
        }

        match self.socket.read_u8().await? {
            0 => {}
            1 => {
                return Ok(None);
            }
            code => {
                return Err(EpmdError::GetNodeInfoError { code });
            }
        }

        Ok(Some(NodeInfo {
            port: self.socket.read_u16().await?,
            node_type: NodeType::try_from(self.socket.read_u8().await?)?,
            protocol: Protocol::try_from(self.socket.read_u8().await?)?,
            highest_version: self.socket.read_u16().await?.try_into()?,
            lowest_version: self.socket.read_u16().await?.try_into()?,
            name: self.socket.read_u16_string().await?,
            extra: self.socket.read_u16_bytes().await?,
        }))
    }

    /// Kills EPMD.
    ///
    /// This request kills the running EPMD.
    /// It is almost never used.
    ///
    /// If EPMD is killed, this method returns `"OK"`.
    pub async fn kill(mut self) -> Result<String, EpmdError> {
        // Request.
        self.socket.write_u16(1).await?;
        self.socket.write_u8(TAG_KILL_REQ).await?;
        self.socket.flush().await?;

        // Response.
        let result = self.socket.read_string().await?;
        Ok(result)
    }

    /// Dumps all data from EPMD.
    ///
    /// This request is not really used, it is to be regarded as a debug feature.
    ///
    /// The result value is a string written for each node kept in the connected EPMD.
    ///
    /// The format of each entry is
    ///
    /// ```shell
    /// "active name ${NODE_NAME} at port ${PORT}, fd = ${FD}\n"
    /// ```
    ///
    /// or
    ///
    /// ```shell
    /// "old/unused name ${NODE_NAME} at port ${PORT}, fd = ${FD}\n"
    /// ```
    pub async fn dump(mut self) -> Result<String, EpmdError> {
        // Request.
        self.socket.write_u16(1).await?;
        self.socket.write_u8(TAG_DUMP_REQ).await?;
        self.socket.flush().await?;

        // Response.
        let _epmd_port = self.socket.read_u32().await?;
        let info = self.socket.read_string().await?;
        Ok(info)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::process::{Child, Command};

    #[derive(Debug)]
    struct TestErlangNode {
        child: Child,
    }

    impl TestErlangNode {
        fn new(name: &str) -> std::io::Result<Self> {
            let child = Command::new("erl")
                .args(&["-sname", name, "-noshell"])
                .spawn()?;
            std::thread::sleep(std::time::Duration::from_secs(1));
            Ok(Self { child })
        }
    }

    impl Drop for TestErlangNode {
        fn drop(&mut self) {
            let _ = self.child.kill();
        }
    }

    async fn epmd_client() -> EpmdClient<smol::net::TcpStream> {
        let stream = smol::net::TcpStream::connect(("127.0.0.1", DEFAULT_EPMD_PORT))
            .await
            .unwrap();
        EpmdClient::new(stream)
    }

    #[test]
    fn epmd_client_works() {
        let node_name = "erl_dist_test";
        let erl_node = TestErlangNode::new(node_name).expect("failed to run a test erlang node");
        smol::block_on(async {
            // Get the information of an existing Erlang node.
            let info = epmd_client()
                .await
                .get_node_info(node_name)
                .await
                .expect("failed to get node info");
            let info = info.expect("no such node");
            assert_eq!(info.name, node_name);

            // Register a new node.
            let client = epmd_client().await;
            let new_node_name = "erl_dist_test_new_node";
            let new_node = NodeInfo {
                name: new_node_name.to_owned(),
                port: 3000,
                node_type: NodeType::Hidden,
                protocol: Protocol::TcpIpV4,
                highest_version: 6,
                lowest_version: 5,
                extra: Vec::new(),
            };
            let (stream, _creation) = client
                .register(new_node)
                .await
                .expect("failed to register a new node");

            // Get the information of the newly added Erlang node.
            let info = epmd_client()
                .await
                .get_node_info(new_node_name)
                .await
                .expect("failed to get node info");
            let info = info.expect("no such node");
            assert_eq!(info.name, new_node_name);

            // Deregister the node.
            std::mem::drop(stream);
            let info = epmd_client()
                .await
                .get_node_info(new_node_name)
                .await
                .expect("failed to get node info");
            assert!(info.is_none());
        });
        std::mem::drop(erl_node);
    }
}
<|endoftext|>


<commit_before>use std::io::{Read, Seek};
use std::path::Path;
use std::fs;

use truetype::{self, Fixed, Tag};

use Result;
use font::Font;

/// A font file.
pub struct File {
    /// Fonts.
    pub fonts: Vec<Font>,
}

impl File {
    /// Open a file.
    #[inline]
    pub fn open<T: AsRef<Path>>(path: T) -> Result<File> {
        let mut file = try!(fs::File::open(path));
        File::read(&mut file)
    }

    /// Read a file.
    pub fn read<T: Read + Seek>(tape: &mut T) -> Result<File> {
        match try!(truetype::Tape::peek::<Fixed>(tape)) {
            Fixed(0x00010000) => {},
            version => {
                let tag = Tag::from(version);
                if tag == Tag::from(b"OTTO") {
                } else if tag == Tag::from(b"ttcf") {
                    raise!("TrueType collections are not supported yet");
                } else {
                    raise!("the font format is invalid");
                }
            },
        }
        Ok(File { fonts: vec![try!(Font::read(tape))] })
    }
}
<commit_msg>Adjust a description<commit_after>use std::io::{Read, Seek};
use std::path::Path;
use std::fs;

use truetype::{self, Fixed, Tag};

use Result;
use font::Font;

/// A file.
pub struct File {
    /// Fonts.
    pub fonts: Vec<Font>,
}

impl File {
    /// Open a file.
    #[inline]
    pub fn open<T: AsRef<Path>>(path: T) -> Result<File> {
        let mut file = try!(fs::File::open(path));
        File::read(&mut file)
    }

    /// Read a file.
    pub fn read<T: Read + Seek>(tape: &mut T) -> Result<File> {
        match try!(truetype::Tape::peek::<Fixed>(tape)) {
            Fixed(0x00010000) => {},
            version => {
                let tag = Tag::from(version);
                if tag == Tag::from(b"OTTO") {
                } else if tag == Tag::from(b"ttcf") {
                    raise!("TrueType collections are not supported yet");
                } else {
                    raise!("the font format is invalid");
                }
            },
        }
        Ok(File { fonts: vec![try!(Font::read(tape))] })
    }
}
<|endoftext|>