<commit_before>
use clap::*;
use common;

pub fn get() -> App<'static, 'static> {
    App::new("multirust-rs")
        .version(common::version())
        .author("Diggory Blake")
        .about("Port of multirust to rust")
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::ArgRequiredElseHelp)
        .arg(
            Arg::with_name("verbose")
                .short("v")
                .long("verbose")
                .help("Enable verbose output")
        )
        .subcommand(
            SubCommand::with_name("default")
                .about("Set the default toolchain.")
                .after_help(
r"Sets the default toolchain to the one specified. If the toolchain is
not already installed then it is first installed.

If the toolchain is already installed then it is not reinstalled,
though if installing a custom toolchain with --copy-local,
--link-local, or --installer then the toolchain is always
reinstalled.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .args(&install_args())
                .group(install_group())
        )
        .subcommand(
            SubCommand::with_name("override")
                .about("Set the toolchain override.")
                .after_help(
r"Sets the toolchain that will be used when working at or below the
current directory. If the toolchain is not already installed then it
is first installed.

If the toolchain is already installed then it is not reinstalled,
though if installing a custom toolchain with --copy-local,
--link-local, or --installer then the toolchain is always
reinstalled.

To remove an existing override use `multirust remove-override`.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .args(&install_args())
                .group(install_group())
        )
        .subcommand(
            SubCommand::with_name("update")
                .about("Install or update a given toolchain.")
                .after_help(
r"With no toolchain specified, the update command updates each of the
stable, beta, and nightly toolchains from the official release
channels, plus any other installed toolchains.
"
                )
                .arg(Arg::with_name("toolchain").required(false))
                .args(&install_args())
                .group(install_group())
        )
        .subcommand(
            SubCommand::with_name("show-override")
                .about("Show information about the current override.")
        )
        .subcommand(
            SubCommand::with_name("show-default")
                .about("Show information about the current default.")
        )
        .subcommand(
            SubCommand::with_name("list-overrides")
                .about("List all overrides.")
        )
        .subcommand(
            SubCommand::with_name("list-toolchains")
                .about("List all installed toolchains.")
        )
        .subcommand(
            SubCommand::with_name("remove-override")
                .about("Remove an override.")
                .after_help(
r"Removes the override for the current directory, or the named
override if one is provided.
"
                )
                .arg(Arg::with_name("override").required(false))
        )
        .subcommand(
            SubCommand::with_name("remove-toolchain")
                .about("Uninstall a toolchain.")
                .after_help(
r"Uninstalls an installed toolchain.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
        )
        .subcommand(
            SubCommand::with_name("list-targets")
                .about("List targets available to install")
                .after_help(
r"List the targets available to an installed toolchain.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
        )
        .subcommand(
            SubCommand::with_name("add-target")
                .about("Add additional compilation targets to an existing toolchain")
                .after_help(
r"Adds the standard library for a given platform to an existing
installation.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .arg(Arg::with_name("target").required(true))
        )
        .subcommand(
            SubCommand::with_name("remove-target")
                .about("Removes compilation targets from an existing toolchain")
                .after_help(
r"Removes the standard library for a given platform.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .arg(Arg::with_name("target").required(true))
        )
        .subcommand(
            SubCommand::with_name("run")
                .setting(AppSettings::TrailingVarArg)
                .about("Run a command.")
                .after_help(
r"Configures an environment to use the given toolchain and then runs
the specified program. The command may be any program, not just
rustc or cargo. This can be used for testing arbitrary toolchains
without setting an override.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .arg(Arg::with_name("command").required(true).multiple(true))
        )
        .subcommand(
            SubCommand::with_name("proxy")
                .setting(AppSettings::TrailingVarArg)
                .about("Proxy a command.")
                .after_help(
r"Configures an environment to use the correct toolchain for the
current directory, and then runs the specified program. The command
may be any program, not just rustc or cargo.
"
                )
                .arg(Arg::with_name("command").required(true).multiple(true))
        )
        .subcommand(
            SubCommand::with_name("delete-data")
                .about("Delete all user metadata.")
                .after_help(
r"Removes all installed toolchains, overrides, and aliases for the
current user.

Prompts for confirmation, unless disabled.

Does not uninstall multirust.
"
                )
                .arg(Arg::with_name("no-prompt").short("y").help("Disable confirmation prompt."))
        )
        .subcommand(
            SubCommand::with_name("upgrade-data")
                .about("Upgrade the ~/.multirust directory.")
                .after_help(
r"Upgrades the ~/.multirust directory from previous versions.
"
                )
        )
        .subcommand(
            SubCommand::with_name("self")
                .about("Commands for managing multirust itself.")
                .subcommand(
                    SubCommand::with_name("uninstall")
                        .about("Uninstalls multirust.")
                        .arg(Arg::with_name("no-prompt").short("y").help("Disable confirmation prompt."))
                )
                .subcommand(
                    SubCommand::with_name("update")
                        .about("Updates multirust.")
                )
        )
        .subcommand(
            SubCommand::with_name("doc")
                .about("Open the documentation for the current toolchain.")
                .after_help(
r"Opens the documentation for the currently active toolchain with the
default browser.

By default, it opens the documentation index. Use the various flags to
open specific pieces of documentation.
"
                )
                .arg(Arg::with_name("book").long("book").help("The Rust Programming Language book"))
                .arg(Arg::with_name("std").long("std").help("Standard library API documentation"))
                .group(ArgGroup::with_name("page").args(&["book", "std"]))
        )
        .subcommand(
            SubCommand::with_name("which")
                .about("Report location of the currently active Rust tool.")
                .arg(Arg::with_name("binary").required(true))
        )
}

fn install_args() -> Vec<Arg<'static, 'static>> {
    vec![
        Arg::with_name("copy-local")
            .long("copy-local")
            .help(r"
             Install by copying a local toolchain. This will
             copy the toolchain from a local directory to a
             directory in multirust's home.
             ")
            .takes_value(true)
            .value_name("toolchain-path")
            .number_of_values(1),
        Arg::with_name("link-local")
            .long("link-local")
            .help(r"
             Install by linking to a local toolchain. This
             will create a soft link to the local toolchain
             in multirust's home.
             ")
            .takes_value(true)
            .value_name("toolchain-path")
            .number_of_values(1),
        Arg::with_name("installer")
            .long("installer")
            .help(r"
             Allows arbitrary builds of rust to be installed
             from a custom-built installer, either from the
             local filesystem or the network. Custom
             installers are neither checksum nor
             signature-verified.

             If multiple installers are specified then they
             are all installed to the same location. This can
             make installing cargo easier since otherwise it
             would need to be combined with rustc into a
             single installer through the rust-packaging
             project.
             ")
            .takes_value(true)
            .value_name("toolchain-path")
            .number_of_values(1)
            .multiple(true),
    ]
}

fn install_group() -> ArgGroup<'static> {
    ArgGroup::with_name("toolchain-source")
        .arg("copy-local")
        .arg("link-local")
        .arg("installer")
        .requires("toolchain")
}
<commit_msg>Add --yes as alias for -y<commit_after>
use clap::*;
use common;

pub fn get() -> App<'static, 'static> {
    App::new("multirust-rs")
        .version(common::version())
        .author("Diggory Blake")
        .about("Port of multirust to rust")
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::ArgRequiredElseHelp)
        .arg(
            Arg::with_name("verbose")
                .short("v")
                .long("verbose")
                .help("Enable verbose output")
        )
        .subcommand(
            SubCommand::with_name("default")
                .about("Set the default toolchain.")
                .after_help(
r"Sets the default toolchain to the one specified. If the toolchain is
not already installed then it is first installed.

If the toolchain is already installed then it is not reinstalled,
though if installing a custom toolchain with --copy-local,
--link-local, or --installer then the toolchain is always
reinstalled.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .args(&install_args())
                .group(install_group())
        )
        .subcommand(
            SubCommand::with_name("override")
                .about("Set the toolchain override.")
                .after_help(
r"Sets the toolchain that will be used when working at or below the
current directory. If the toolchain is not already installed then it
is first installed.

If the toolchain is already installed then it is not reinstalled,
though if installing a custom toolchain with --copy-local,
--link-local, or --installer then the toolchain is always
reinstalled.

To remove an existing override use `multirust remove-override`.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .args(&install_args())
                .group(install_group())
        )
        .subcommand(
            SubCommand::with_name("update")
                .about("Install or update a given toolchain.")
                .after_help(
r"With no toolchain specified, the update command updates each of the
stable, beta, and nightly toolchains from the official release
channels, plus any other installed toolchains.
"
                )
                .arg(Arg::with_name("toolchain").required(false))
                .args(&install_args())
                .group(install_group())
        )
        .subcommand(
            SubCommand::with_name("show-override")
                .about("Show information about the current override.")
        )
        .subcommand(
            SubCommand::with_name("show-default")
                .about("Show information about the current default.")
        )
        .subcommand(
            SubCommand::with_name("list-overrides")
                .about("List all overrides.")
        )
        .subcommand(
            SubCommand::with_name("list-toolchains")
                .about("List all installed toolchains.")
        )
        .subcommand(
            SubCommand::with_name("remove-override")
                .about("Remove an override.")
                .after_help(
r"Removes the override for the current directory, or the named
override if one is provided.
"
                )
                .arg(Arg::with_name("override").required(false))
        )
        .subcommand(
            SubCommand::with_name("remove-toolchain")
                .about("Uninstall a toolchain.")
                .after_help(
r"Uninstalls an installed toolchain.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
        )
        .subcommand(
            SubCommand::with_name("list-targets")
                .about("List targets available to install")
                .after_help(
r"List the targets available to an installed toolchain.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
        )
        .subcommand(
            SubCommand::with_name("add-target")
                .about("Add additional compilation targets to an existing toolchain")
                .after_help(
r"Adds the standard library for a given platform to an existing
installation.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .arg(Arg::with_name("target").required(true))
        )
        .subcommand(
            SubCommand::with_name("remove-target")
                .about("Removes compilation targets from an existing toolchain")
                .after_help(
r"Removes the standard library for a given platform.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .arg(Arg::with_name("target").required(true))
        )
        .subcommand(
            SubCommand::with_name("run")
                .setting(AppSettings::TrailingVarArg)
                .about("Run a command.")
                .after_help(
r"Configures an environment to use the given toolchain and then runs
the specified program. The command may be any program, not just
rustc or cargo. This can be used for testing arbitrary toolchains
without setting an override.
"
                )
                .arg(Arg::with_name("toolchain").required(true))
                .arg(Arg::with_name("command").required(true).multiple(true))
        )
        .subcommand(
            SubCommand::with_name("proxy")
                .setting(AppSettings::TrailingVarArg)
                .about("Proxy a command.")
                .after_help(
r"Configures an environment to use the correct toolchain for the
current directory, and then runs the specified program. The command
may be any program, not just rustc or cargo.
"
                )
                .arg(Arg::with_name("command").required(true).multiple(true))
        )
        .subcommand(
            SubCommand::with_name("delete-data")
                .about("Delete all user metadata.")
                .after_help(
r"Removes all installed toolchains, overrides, and aliases for the
current user.

Prompts for confirmation, unless disabled.

Does not uninstall multirust.
"
                )
                .arg(
                   Arg::with_name("no-prompt")
                      .short("y")
                      .long("yes")
                      .help("Disable confirmation prompt.")
                )
        )
        .subcommand(
            SubCommand::with_name("upgrade-data")
                .about("Upgrade the ~/.multirust directory.")
                .after_help(
r"Upgrades the ~/.multirust directory from previous versions.
"
                )
        )
        .subcommand(
            SubCommand::with_name("self")
                .about("Commands for managing multirust itself.")
                .subcommand(
                    SubCommand::with_name("uninstall")
                        .about("Uninstalls multirust.")
                        .arg(
                           Arg::with_name("no-prompt")
                              .short("y")
                              .long("yes")
                              .help("Disable confirmation prompt.")
                        )
                )
                .subcommand(
                    SubCommand::with_name("update")
                        .about("Updates multirust.")
                )
        )
        .subcommand(
            SubCommand::with_name("doc")
                .about("Open the documentation for the current toolchain.")
                .after_help(
r"Opens the documentation for the currently active toolchain with the
default browser.

By default, it opens the documentation index. Use the various flags to
open specific pieces of documentation.
"
                )
                .arg(Arg::with_name("book").long("book").help("The Rust Programming Language book"))
                .arg(Arg::with_name("std").long("std").help("Standard library API documentation"))
                .group(ArgGroup::with_name("page").args(&["book", "std"]))
        )
        .subcommand(
            SubCommand::with_name("which")
                .about("Report location of the currently active Rust tool.")
                .arg(Arg::with_name("binary").required(true))
        )
}

fn install_args() -> Vec<Arg<'static, 'static>> {
    vec![
        Arg::with_name("copy-local")
            .long("copy-local")
            .help(r"
             Install by copying a local toolchain. This will
             copy the toolchain from a local directory to a
             directory in multirust's home.
             ")
            .takes_value(true)
            .value_name("toolchain-path")
            .number_of_values(1),
        Arg::with_name("link-local")
            .long("link-local")
            .help(r"
             Install by linking to a local toolchain. This
             will create a soft link to the local toolchain
             in multirust's home.
             ")
            .takes_value(true)
            .value_name("toolchain-path")
            .number_of_values(1),
        Arg::with_name("installer")
            .long("installer")
            .help(r"
             Allows arbitrary builds of rust to be installed
             from a custom-built installer, either from the
             local filesystem or the network. Custom
             installers are neither checksum nor
             signature-verified.

             If multiple installers are specified then they
             are all installed to the same location. This can
             make installing cargo easier since otherwise it
             would need to be combined with rustc into a
             single installer through the rust-packaging
             project.
             ")
            .takes_value(true)
            .value_name("toolchain-path")
            .number_of_values(1)
            .multiple(true),
    ]
}

fn install_group() -> ArgGroup<'static> {
    ArgGroup::with_name("toolchain-source")
        .arg("copy-local")
        .arg("link-local")
        .arg("installer")
        .requires("toolchain")
}
<|endoftext|>


<commit_before>use crate::prelude::*;

#[derive(Debug, Clone, Copy, Hash, PartialOrd, PartialEq)]
#[repr(C)]
pub enum TypeSystemName
{
    Automation = 0,
    Raw = 1,
}

/// Common trait for type systems.
pub trait TypeSystem: Clone + Copy
{
    const AUTOMATION: TypeSystemName = TypeSystemName::Automation;
    const RAW: TypeSystemName = TypeSystemName::Raw;

    fn key() -> TypeSystemName;
}

/// Automation type system.
#[derive(Clone, Copy)]
pub struct AutomationTypeSystem;
impl TypeSystem for AutomationTypeSystem
{
    fn key() -> TypeSystemName
    {
        TypeSystemName::Automation
    }
}

/// Raw type system.
#[derive(Clone, Copy)]
pub struct RawTypeSystem;
impl TypeSystem for RawTypeSystem
{
    fn key() -> TypeSystemName
    {
        TypeSystemName::Raw
    }
}

/// Defines a type that has identical representation for both input and output directions.
pub trait ForeignType
{
    /// The name of the type.
    fn type_name() -> &'static str;
    fn indirection_level() -> u32
    {
        0
    }
}

/// Defines a type that may be used as a parameter type in Intercom interfaces.
///
/// # Safety
///
/// Implementing this trait allows Intercom to use the type as an input type.
/// This trait will be used within the code generated in the procedural macros.
/// It is important to ensure this trait is implemented in such a way that its
/// use in the macros is sound.
pub unsafe trait ExternInput<TS: TypeSystem>: Sized
{
    type ForeignType: ForeignType;

    type Lease;

    /// # Safety
    ///
    /// The returned `ForeignType` value is valid only as long as the `Lease`
    /// is held.
    unsafe fn into_foreign_parameter(self) -> ComResult<(Self::ForeignType, Self::Lease)>;

    type Owned;

    /// # Safety
    ///
    /// The validity of the returned `Owned` value depends on the source type.
    /// In general it shouldn't be used past the lifetime of the `source`
    /// reference.
    unsafe fn from_foreign_parameter(source: Self::ForeignType) -> ComResult<Self::Owned>;
}

/// Defines a type that may be used as an output type in Intercom interfaces.
///
/// # Safety
///
/// Implementing this trait allows Intercom to use the type as an output type.
/// This trait will be used within the code generated in the procedural macros.
/// It is important to ensure this trait is implemented in such a way that its
/// use in the macros is sound.
pub unsafe trait ExternOutput<TS: TypeSystem>: Sized
{
    type ForeignType: ForeignType;

    fn into_foreign_output(self) -> ComResult<Self::ForeignType>;

    /// # Safety
    ///
    /// The source ownership is transferred to the function invoker. In case of
    /// pointers, the function (or the `Self` type) is given the ownership of
    /// the memory. The caller must ensure that it owns the source parameter
    /// and is allowed to pass the ownership in this way.
    unsafe fn from_foreign_output(source: Self::ForeignType) -> ComResult<Self>;
}

/// A quick macro for implementing ExternInput/etc. for various basic types
/// that should represent themselves.
macro_rules! self_extern {
    ( $t:ty ) => {
        impl ForeignType for $t
        {
            /// The default name is the name of the type.
            fn type_name() -> &'static str
            {
                stringify!($t)
            }
        }

        unsafe impl<TS: TypeSystem> ExternInput<TS> for $t
        {
            type ForeignType = $t;
            type Lease = ();
            unsafe fn into_foreign_parameter(self) -> ComResult<(Self::ForeignType, ())>
            {
                Ok((self, ()))
            }

            type Owned = Self;
            unsafe fn from_foreign_parameter(source: Self::ForeignType) -> ComResult<Self::Owned>
            {
                Ok(source)
            }
        }

        unsafe impl<TS: TypeSystem> ExternOutput<TS> for $t
        {
            type ForeignType = $t;
            fn into_foreign_output(self) -> ComResult<Self::ForeignType>
            {
                Ok(self)
            }

            unsafe fn from_foreign_output(source: Self::ForeignType) -> ComResult<Self>
            {
                Ok(source)
            }
        }
    };
}

// Define all types that should have built-in Self extern type.
self_extern!(());
self_extern!(i8);
self_extern!(i16);
self_extern!(i32);
self_extern!(i64);
self_extern!(isize);
self_extern!(u8);
self_extern!(u16);
self_extern!(u32);
self_extern!(u64);
self_extern!(usize);
self_extern!(f32);
self_extern!(f64);
self_extern!(bool);

use crate::raw::HRESULT;
self_extern!(HRESULT);

use crate::GUID;
self_extern!(GUID);

self_extern!(TypeSystemName);

impl ForeignType for libc::c_void
{
    fn type_name() -> &'static str
    {
        "void"
    }
}

unsafe impl<TS: TypeSystem, TPtr: ForeignType + ?Sized> ExternOutput<TS> for *mut TPtr
{
    type ForeignType = Self;
    fn into_foreign_output(self) -> ComResult<Self::ForeignType>
    {
        Ok(self)
    }

    unsafe fn from_foreign_output(source: Self::ForeignType) -> ComResult<Self>
    {
        Ok(source)
    }
}

unsafe impl<TS: TypeSystem, TPtr: ForeignType + ?Sized> ExternOutput<TS> for *const TPtr
{
    type ForeignType = Self;
    fn into_foreign_output(self) -> ComResult<Self::ForeignType>
    {
        Ok(self)
    }

    unsafe fn from_foreign_output(source: Self::ForeignType) -> ComResult<Self>
    {
        Ok(source)
    }
}

unsafe impl<TS: TypeSystem, TPtr: ForeignType + ?Sized> ExternInput<TS> for *mut TPtr
{
    type ForeignType = Self;
    type Lease = ();
    unsafe fn into_foreign_parameter(self) -> ComResult<(Self::ForeignType, ())>
    {
        Ok((self, ()))
    }

    type Owned = Self;
    unsafe fn from_foreign_parameter(source: Self::ForeignType) -> ComResult<Self::Owned>
    {
        Ok(source)
    }
}

unsafe impl<TS: TypeSystem, TPtr: ForeignType + ?Sized> ExternInput<TS> for *const TPtr
{
    type ForeignType = Self;
    type Lease = ();
    unsafe fn into_foreign_parameter(self) -> ComResult<(Self::ForeignType, ())>
    {
        Ok((self, ()))
    }

    type Owned = Self;
    unsafe fn from_foreign_parameter(source: Self::ForeignType) -> ComResult<Self::Owned>
    {
        Ok(source)
    }
}

impl<TPtr: ForeignType + ?Sized> ForeignType for *mut TPtr
{
    fn type_name() -> &'static str
    {
        <TPtr as ForeignType>::type_name()
    }

    fn indirection_level() -> u32
    {
        <TPtr as ForeignType>::indirection_level() + 1
    }
}

impl<TPtr: ForeignType + ?Sized> ForeignType for *const TPtr
{
    fn type_name() -> &'static str
    {
        <TPtr as ForeignType>::type_name()
    }

    fn indirection_level() -> u32
    {
        <TPtr as ForeignType>::indirection_level() + 1
    }
}

impl<TS: TypeSystem, I: crate::ComInterface + ?Sized> ForeignType
    for crate::raw::InterfacePtr<TS, I>
where
    I: ForeignType,
{
    /// The name of the type.
    fn type_name() -> &'static str
    {
        <I as ForeignType>::type_name()
    }
    fn indirection_level() -> u32
    {
        <I as ForeignType>::indirection_level() + 1
    }
}

/// Defines the uninitialized values for out parameters when calling into
/// Intercom interfaces.
pub trait ExternDefault
{
    /// # Safety
    ///
    /// This results in zeroed values. This should only be used for types that
    /// are okay being zeroed (mainly `#[repr(C)]` types).
    unsafe fn extern_default() -> Self;
}

impl<T> ExternDefault for T
{
    default unsafe fn extern_default() -> Self
    {
        std::mem::zeroed()
    }
}

impl<TPtr> ExternDefault for *const TPtr
{
    default unsafe fn extern_default() -> Self
    {
        std::ptr::null()
    }
}
<commit_msg>Implement foreign type on std::ffi::c_void<commit_after>use crate::prelude::*;

#[derive(Debug, Clone, Copy, Hash, PartialOrd, PartialEq)]
#[repr(C)]
pub enum TypeSystemName
{
    Automation = 0,
    Raw = 1,
}

/// Common trait for type systems.
pub trait TypeSystem: Clone + Copy
{
    const AUTOMATION: TypeSystemName = TypeSystemName::Automation;
    const RAW: TypeSystemName = TypeSystemName::Raw;

    fn key() -> TypeSystemName;
}

/// Automation type system.
#[derive(Clone, Copy)]
pub struct AutomationTypeSystem;
impl TypeSystem for AutomationTypeSystem
{
    fn key() -> TypeSystemName
    {
        TypeSystemName::Automation
    }
}

/// Raw type system.
#[derive(Clone, Copy)]
pub struct RawTypeSystem;
impl TypeSystem for RawTypeSystem
{
    fn key() -> TypeSystemName
    {
        TypeSystemName::Raw
    }
}

/// Defines a type that has identical representation for both input and output directions.
pub trait ForeignType
{
    /// The name of the type.
    fn type_name() -> &'static str;
    fn indirection_level() -> u32
    {
        0
    }
}

/// Defines a type that may be used as a parameter type in Intercom interfaces.
///
/// # Safety
///
/// Implementing this trait allows Intercom to use the type as an input type.
/// This trait will be used within the code generated in the procedural macros.
/// It is important to ensure this trait is implemented in such a way that its
/// use in the macros is sound.
pub unsafe trait ExternInput<TS: TypeSystem>: Sized
{
    type ForeignType: ForeignType;

    type Lease;

    /// # Safety
    ///
    /// The returned `ForeignType` value is valid only as long as the `Lease`
    /// is held.
    unsafe fn into_foreign_parameter(self) -> ComResult<(Self::ForeignType, Self::Lease)>;

    type Owned;

    /// # Safety
    ///
    /// The validity of the returned `Owned` value depends on the source type.
    /// In general it shouldn't be used past the lifetime of the `source`
    /// reference.
    unsafe fn from_foreign_parameter(source: Self::ForeignType) -> ComResult<Self::Owned>;
}

/// Defines a type that may be used as an output type in Intercom interfaces.
///
/// # Safety
///
/// Implementing this trait allows Intercom to use the type as an output type.
/// This trait will be used within the code generated in the procedural macros.
/// It is important to ensure this trait is implemented in such a way that its
/// use in the macros is sound.
pub unsafe trait ExternOutput<TS: TypeSystem>: Sized
{
    type ForeignType: ForeignType;

    fn into_foreign_output(self) -> ComResult<Self::ForeignType>;

    /// # Safety
    ///
    /// The source ownership is transferred to the function invoker. In case of
    /// pointers, the function (or the `Self` type) is given the ownership of
    /// the memory. The caller must ensure that it owns the source parameter
    /// and is allowed to pass the ownership in this way.
    unsafe fn from_foreign_output(source: Self::ForeignType) -> ComResult<Self>;
}

/// A quick macro for implementing ExternInput/etc. for various basic types
/// that should represent themselves.
macro_rules! self_extern {
    ( $t:ty ) => {
        impl ForeignType for $t
        {
            /// The default name is the name of the type.
            fn type_name() -> &'static str
            {
                stringify!($t)
            }
        }

        unsafe impl<TS: TypeSystem> ExternInput<TS> for $t
        {
            type ForeignType = $t;
            type Lease = ();
            unsafe fn into_foreign_parameter(self) -> ComResult<(Self::ForeignType, ())>
            {
                Ok((self, ()))
            }

            type Owned = Self;
            unsafe fn from_foreign_parameter(source: Self::ForeignType) -> ComResult<Self::Owned>
            {
                Ok(source)
            }
        }

        unsafe impl<TS: TypeSystem> ExternOutput<TS> for $t
        {
            type ForeignType = $t;
            fn into_foreign_output(self) -> ComResult<Self::ForeignType>
            {
                Ok(self)
            }

            unsafe fn from_foreign_output(source: Self::ForeignType) -> ComResult<Self>
            {
                Ok(source)
            }
        }
    };
}

// Define all types that should have built-in Self extern type.
self_extern!(());
self_extern!(i8);
self_extern!(i16);
self_extern!(i32);
self_extern!(i64);
self_extern!(isize);
self_extern!(u8);
self_extern!(u16);
self_extern!(u32);
self_extern!(u64);
self_extern!(usize);
self_extern!(f32);
self_extern!(f64);
self_extern!(bool);

use crate::raw::HRESULT;
self_extern!(HRESULT);

use crate::GUID;
self_extern!(GUID);

self_extern!(TypeSystemName);

self_extern!(std::ffi::c_void);

unsafe impl<TS: TypeSystem, TPtr: ForeignType + ?Sized> ExternOutput<TS> for *mut TPtr
{
    type ForeignType = Self;
    fn into_foreign_output(self) -> ComResult<Self::ForeignType>
    {
        Ok(self)
    }

    unsafe fn from_foreign_output(source: Self::ForeignType) -> ComResult<Self>
    {
        Ok(source)
    }
}

unsafe impl<TS: TypeSystem, TPtr: ForeignType + ?Sized> ExternOutput<TS> for *const TPtr
{
    type ForeignType = Self;
    fn into_foreign_output(self) -> ComResult<Self::ForeignType>
    {
        Ok(self)
    }

    unsafe fn from_foreign_output(source: Self::ForeignType) -> ComResult<Self>
    {
        Ok(source)
    }
}

unsafe impl<TS: TypeSystem, TPtr: ForeignType + ?Sized> ExternInput<TS> for *mut TPtr
{
    type ForeignType = Self;
    type Lease = ();
    unsafe fn into_foreign_parameter(self) -> ComResult<(Self::ForeignType, ())>
    {
        Ok((self, ()))
    }

    type Owned = Self;
    unsafe fn from_foreign_parameter(source: Self::ForeignType) -> ComResult<Self::Owned>
    {
        Ok(source)
    }
}

unsafe impl<TS: TypeSystem, TPtr: ForeignType + ?Sized> ExternInput<TS> for *const TPtr
{
    type ForeignType = Self;
    type Lease = ();
    unsafe fn into_foreign_parameter(self) -> ComResult<(Self::ForeignType, ())>
    {
        Ok((self, ()))
    }

    type Owned = Self;
    unsafe fn from_foreign_parameter(source: Self::ForeignType) -> ComResult<Self::Owned>
    {
        Ok(source)
    }
}

impl<TPtr: ForeignType + ?Sized> ForeignType for *mut TPtr
{
    fn type_name() -> &'static str
    {
        <TPtr as ForeignType>::type_name()
    }

    fn indirection_level() -> u32
    {
        <TPtr as ForeignType>::indirection_level() + 1
    }
}

impl<TPtr: ForeignType + ?Sized> ForeignType for *const TPtr
{
    fn type_name() -> &'static str
    {
        <TPtr as ForeignType>::type_name()
    }

    fn indirection_level() -> u32
    {
        <TPtr as ForeignType>::indirection_level() + 1
    }
}

impl<TS: TypeSystem, I: crate::ComInterface + ?Sized> ForeignType
    for crate::raw::InterfacePtr<TS, I>
where
    I: ForeignType,
{
    /// The name of the type.
    fn type_name() -> &'static str
    {
        <I as ForeignType>::type_name()
    }
    fn indirection_level() -> u32
    {
        <I as ForeignType>::indirection_level() + 1
    }
}

/// Defines the uninitialized values for out parameters when calling into
/// Intercom interfaces.
pub trait ExternDefault
{
    /// # Safety
    ///
    /// This results in zeroed values. This should only be used for types that
    /// are okay being zeroed (mainly `#[repr(C)]` types).
    unsafe fn extern_default() -> Self;
}

impl<T> ExternDefault for T
{
    default unsafe fn extern_default() -> Self
    {
        std::mem::zeroed()
    }
}

impl<TPtr> ExternDefault for *const TPtr
{
    default unsafe fn extern_default() -> Self
    {
        std::ptr::null()
    }
}
<|endoftext|>


<commit_before>use hashing::canonical_hash;

const SSZ_CHUNK_SIZE: usize = 128;
const HASHSIZE: usize = 32;

pub trait TreeHash {
    fn hash_tree_root(&self) -> Vec<u8>;
}

/// Returns a 32 byte hash of 'list' - a vector of byte vectors.
/// Note that this will consume 'list'.
pub fn merkle_hash(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
    // flatten list
    let (chunk_size, mut chunkz) = list_to_blob(list);

    // get data_len as bytes. It will hashed will the merkle root
    let datalen = list.len().to_le_bytes();

    // Tree-hash
    while chunkz.len() > HASHSIZE {
        let mut new_chunkz: Vec<u8> = Vec::new();

        for two_chunks in chunkz.chunks(chunk_size * 2) {
            if two_chunks.len() == chunk_size {
                // Odd number of chunks
                let mut c = two_chunks.to_vec();
                c.append(&mut vec![0; SSZ_CHUNK_SIZE]);
                new_chunkz.append(&mut hash(&c));
            } else {
                // Hash two chuncks together
                new_chunkz.append(&mut hash(two_chunks));
            }
            chunk_size = HASHSIZE;
        }
        chunkz = new_chunkz;
    }

    chunkz.append(&mut datalen.to_vec());
    hash(&chunkz)
}

fn list_to_blob(list: &mut Vec<Vec<u8>>) -> (usize, Vec<u8>) {
    let chunk_size = if list.is_empty() {
        SSZ_CHUNK_SIZE
    } else if list[0].len() < SSZ_CHUNK_SIZE {
        let items_per_chunk = SSZ_CHUNK_SIZE / list[0].len();
        items_per_chunk * list[0].len()
    } else {
        list[0].len()
    };

    let mut data = Vec::new();
    if list.is_empty() {
        // handle and empty list
        data.append(&mut vec![0; SSZ_CHUNK_SIZE]);
    } else {
        // just create a blob here; we'll divide into
        // chunked slices when we merklize
        data.reserve(list[0].len() * list.len());
        for item in list.iter_mut() {
            data.append(item);
        }
    }
    (chunk_size, data)
}

pub fn hash(data: &[u8]) -> Vec<u8> {
    canonical_hash(data)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_merkle_hash() {
        let data1 = vec![1; 100];
        let data2 = vec![2; 100];
        let data3 = vec![3; 100];
        let mut list = vec![data1, data2, data3];
        let result = merkle_hash(&mut list);

        //note: should test againt a known test hash value
        assert_eq!(HASHSIZE, result.len());
    }
}
<commit_msg>Minor modification to tree_hash<commit_after>use hashing::canonical_hash;

const SSZ_CHUNK_SIZE: usize = 128;
const HASHSIZE: usize = 32;

pub trait TreeHash {
    fn hash_tree_root(&self) -> Vec<u8>;
}

/// Returns a 32 byte hash of 'list' - a vector of byte vectors.
/// Note that this will consume 'list'.
pub fn merkle_hash(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
    // flatten list
    let (mut chunk_size, mut chunkz) = list_to_blob(list);

    // get data_len as bytes. It will hashed will the merkle root
    let datalen = list.len().to_le_bytes();

    // Tree-hash
    while chunkz.len() > HASHSIZE {
        let mut new_chunkz: Vec<u8> = Vec::new();

        for two_chunks in chunkz.chunks(chunk_size * 2) {
            if two_chunks.len() == chunk_size {
                // Odd number of chunks
                let mut c = two_chunks.to_vec();
                c.append(&mut vec![0; SSZ_CHUNK_SIZE]);
                new_chunkz.append(&mut hash(&c));
            } else {
                // Hash two chuncks together
                new_chunkz.append(&mut hash(two_chunks));
            }
            chunk_size = HASHSIZE;
        }
        chunkz = new_chunkz;
    }

    chunkz.append(&mut datalen.to_vec());
    hash(&chunkz)
}

fn list_to_blob(list: &mut Vec<Vec<u8>>) -> (usize, Vec<u8>) {
    let chunk_size = if list.is_empty() {
        SSZ_CHUNK_SIZE
    } else if list[0].len() < SSZ_CHUNK_SIZE {
        let items_per_chunk = SSZ_CHUNK_SIZE / list[0].len();
        items_per_chunk * list[0].len()
    } else {
        list[0].len()
    };

    let mut data = Vec::new();
    if list.is_empty() {
        // handle and empty list
        data.append(&mut vec![0; SSZ_CHUNK_SIZE]);
    } else {
        // just create a blob here; we'll divide into
        // chunked slices when we merklize
        data.reserve(list[0].len() * list.len());
        for item in list.iter_mut() {
            data.append(item);
        }
    }
    (chunk_size, data)
}

pub fn hash(data: &[u8]) -> Vec<u8> {
    canonical_hash(data)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_merkle_hash() {
        let data1 = vec![1; 100];
        let data2 = vec![2; 100];
        let data3 = vec![3; 100];
        let mut list = vec![data1, data2, data3];
        let result = merkle_hash(&mut list);

        //note: should test againt a known test hash value
        assert_eq!(HASHSIZE, result.len());
    }
}
<|endoftext|>


<commit_before>use serde::ser::Serialize;
use serde::ser::Serializer;

trait EntryType: Serialize {
    fn type_name() -> &'static str;
    fn remote_ip(&self) -> &str;
    fn timestamp(&self) -> f64;
    fn request_method(&self) -> &str;
    fn request_url(&self) -> &str;
    fn request_protocol(&self) -> &str;
    fn response_status(&self) -> Option<u32>;
    fn response_bytes(&self) -> Option<u64>;
}

#[derive(Serialize, Debug)]
struct ClientAccessLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    request_type: &'a str,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    handling: &'a str,
    request: HttpRequestLogEntry<'a>,
    response: HttpResponseLogEntry<'a>,
    process_duration: Option<f64>,
    fetch_duration: Option<f64>,
    ttfb_duration: f64,
    serve_duration: f64,
    recv_header_bytes: u64,
    recv_body_bytes: u64,
    recv_total_bytes: u64,
    sent_header_bytes: u64,
    sent_body_bytes: u64,
    sent_total_bytes: u64,
    esi_count: usize,
    restart_count: usize,
    restart_log: Option<LogBook<'a>>,
    log: LogBook<'a>,
    #[serde(skip_serializing_if="Option::is_none")]
    request_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    response_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    log_vars_index: Option<Index<'a>>,
}

impl<'a> EntryType for ClientAccessLogEntry<'a> {
    fn type_name() -> &'static str {
        "client_access"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        Some(self.response.status)
    }
    fn response_bytes(&self) -> Option<u64> {
        Some(self.sent_total_bytes)
    }
}

#[derive(Serialize, Debug)]
struct BackendAccessLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    handling: &'a str,
    request: HttpRequestLogEntry<'a>,
    response: Option<HttpResponseLogEntry<'a>>,
    send_duration: f64,
    wait_duration: Option<f64>,
    ttfb_duration: Option<f64>,
    fetch_duration: Option<f64>,
    sent_header_bytes: Option<u64>,
    sent_body_bytes: Option<u64>,
    sent_total_bytes: Option<u64>,
    recv_header_bytes: Option<u64>,
    recv_body_bytes: Option<u64>,
    recv_total_bytes: Option<u64>,
    retry: usize,
    backend_connection: Option<BackendConnectionLogEntry<'a>>,
    cache_object: Option<CacheObjectLogEntry<'a>>,
    log: LogBook<'a>,
    #[serde(skip_serializing_if="Option::is_none")]
    request_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    response_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    log_vars_index: Option<Index<'a>>,
}

impl<'a> EntryType for BackendAccessLogEntry<'a> {
    fn type_name() -> &'static str {
        "backend_access"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        self.response.as_ref().map(|r| r.status).or(Some(503)) // no response
    }
    fn response_bytes(&self) -> Option<u64> {
        self.recv_total_bytes
    }
}

#[derive(Serialize, Debug)]
struct PipeSessionLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    backend_connection: BackendConnectionLogEntry<'a>,
    request: HttpRequestLogEntry<'a>,
    backend_request: HttpRequestLogEntry<'a>,
    process_duration: Option<f64>,
    ttfb_duration: f64,
    recv_total_bytes: u64,
    sent_total_bytes: u64,
    log: LogBook<'a>,
    #[serde(skip_serializing_if="Option::is_none")]
    request_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    backend_request_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    log_vars_index: Option<Index<'a>>,
}

impl<'a> EntryType for PipeSessionLogEntry<'a> {
    fn type_name() -> &'static str {
        "pipe_session"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        None
    }
    fn response_bytes(&self) -> Option<u64> {
        Some(self.sent_total_bytes)
    }
}

#[derive(Serialize, Debug)]
struct AddressLogEntry<'a> {
    ip: &'a str,
    port: u16,
}

#[derive(Serialize, Debug)]
struct HttpRequestLogEntry<'a> {
    protocol: &'a str,
    method: &'a str,
    url: &'a str,
    headers: &'a [(String, String)],
}

#[derive(Serialize, Debug)]
struct HttpResponseLogEntry<'a> {
    status: u32,
    reason: &'a str,
    protocol: &'a str,
    headers: &'a [(String, String)],
}

#[derive(Debug)]
struct LogBook<'a>(&'a [LogEntry]);

#[derive(Serialize, Debug)]
struct LogBookEntry<'a> {
    entry_type: &'a str,
    message: &'a str,
    #[serde(skip_serializing_if="Option::is_none")]
    detail: Option<&'a str>,
}

impl<'a> Serialize for LogBook<'a> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        let mut state = try!(serializer.serialize_seq(Some(self.0.len())));
        for log_entry in self.0 {
            let (entry_type, message, detail) = match log_entry {
                &LogEntry::Vcl(ref msg) => ("VCL", msg.as_str(), None),
                &LogEntry::Debug(ref msg) => ("Debug", msg.as_str(), None),
                &LogEntry::Error(ref msg) => ("Error", msg.as_str(), None),
                &LogEntry::FetchError(ref msg) => ("Fetch Error", msg.as_str(), None),
                &LogEntry::Warning(ref msg) => ("Warning", msg.as_str(), None),
                &LogEntry::Acl(ref result, ref name, ref addr) => match result.as_str() {
                    "MATCH" => ("ACL Match", name.as_str(), addr.as_ref().map(String::as_str)),
                    "NO_MATCH" => ("ACL No Match", name.as_str(), addr.as_ref().map(String::as_str)),
                    _ => ("ACL Other", result.as_str(), Some(name.as_str())),
                },
            };

            try!(serializer.serialize_seq_elt(&mut state, &LogBookEntry {
                entry_type: entry_type,
                message: message,
                detail: detail,
            }));
        }
        try!(serializer.serialize_seq_end(state));
        Ok(())
    }
}

#[derive(Debug)]
struct Index<'a>(&'a LinkedHashMap<String, Vec<String>>);

impl<'a> Serialize for Index<'a> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        let mut state = try!(serializer.serialize_map(Some(self.0.len())));
        for (ref key, ref values) in self.0 {
            try!(serializer.serialize_map_key(&mut state, key));
            try!(serializer.serialize_map_value(&mut state, values));
        }
        try!(serializer.serialize_map_end(state));
        Ok(())
    }
}

#[derive(Serialize, Debug)]
struct CacheObjectLogEntry<'a> {
    storage_type: &'a str,
    storage_name: &'a str,
    ttl_duration: Option<f64>,
    grace_duration: Option<f64>,
    keep_duration: Option<f64>,
    since_timestamp: f64,
    origin_timestamp: f64,
    fetch_mode: &'a str,
    fetch_streamed: bool,
    response: HttpResponseLogEntry<'a>,
}

#[derive(Serialize, Debug)]
struct BackendConnectionLogEntry<'a> {
    fd: isize,
    name: &'a str,
    remote_address: AddressLogEntry<'a>,
    local_address: AddressLogEntry<'a>,
}
<commit_msg>use body bytes for NCSA format where available<commit_after>use serde::ser::Serialize;
use serde::ser::Serializer;

trait EntryType: Serialize {
    fn type_name() -> &'static str;
    fn remote_ip(&self) -> &str;
    fn timestamp(&self) -> f64;
    fn request_method(&self) -> &str;
    fn request_url(&self) -> &str;
    fn request_protocol(&self) -> &str;
    fn response_status(&self) -> Option<u32>;
    fn response_bytes(&self) -> Option<u64>;
}

#[derive(Serialize, Debug)]
struct ClientAccessLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    request_type: &'a str,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    handling: &'a str,
    request: HttpRequestLogEntry<'a>,
    response: HttpResponseLogEntry<'a>,
    process_duration: Option<f64>,
    fetch_duration: Option<f64>,
    ttfb_duration: f64,
    serve_duration: f64,
    recv_header_bytes: u64,
    recv_body_bytes: u64,
    recv_total_bytes: u64,
    sent_header_bytes: u64,
    sent_body_bytes: u64,
    sent_total_bytes: u64,
    esi_count: usize,
    restart_count: usize,
    restart_log: Option<LogBook<'a>>,
    log: LogBook<'a>,
    #[serde(skip_serializing_if="Option::is_none")]
    request_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    response_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    log_vars_index: Option<Index<'a>>,
}

impl<'a> EntryType for ClientAccessLogEntry<'a> {
    fn type_name() -> &'static str {
        "client_access"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        Some(self.response.status)
    }
    fn response_bytes(&self) -> Option<u64> {
        Some(self.sent_body_bytes)
    }
}

#[derive(Serialize, Debug)]
struct BackendAccessLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    handling: &'a str,
    request: HttpRequestLogEntry<'a>,
    response: Option<HttpResponseLogEntry<'a>>,
    send_duration: f64,
    wait_duration: Option<f64>,
    ttfb_duration: Option<f64>,
    fetch_duration: Option<f64>,
    sent_header_bytes: Option<u64>,
    sent_body_bytes: Option<u64>,
    sent_total_bytes: Option<u64>,
    recv_header_bytes: Option<u64>,
    recv_body_bytes: Option<u64>,
    recv_total_bytes: Option<u64>,
    retry: usize,
    backend_connection: Option<BackendConnectionLogEntry<'a>>,
    cache_object: Option<CacheObjectLogEntry<'a>>,
    log: LogBook<'a>,
    #[serde(skip_serializing_if="Option::is_none")]
    request_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    response_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    log_vars_index: Option<Index<'a>>,
}

impl<'a> EntryType for BackendAccessLogEntry<'a> {
    fn type_name() -> &'static str {
        "backend_access"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        self.response.as_ref().map(|r| r.status).or(Some(503)) // no response
    }
    fn response_bytes(&self) -> Option<u64> {
        self.recv_body_bytes
    }
}

#[derive(Serialize, Debug)]
struct PipeSessionLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    backend_connection: BackendConnectionLogEntry<'a>,
    request: HttpRequestLogEntry<'a>,
    backend_request: HttpRequestLogEntry<'a>,
    process_duration: Option<f64>,
    ttfb_duration: f64,
    recv_total_bytes: u64,
    sent_total_bytes: u64,
    log: LogBook<'a>,
    #[serde(skip_serializing_if="Option::is_none")]
    request_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    backend_request_header_index: Option<Index<'a>>,
    #[serde(skip_serializing_if="Option::is_none")]
    log_vars_index: Option<Index<'a>>,
}

impl<'a> EntryType for PipeSessionLogEntry<'a> {
    fn type_name() -> &'static str {
        "pipe_session"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        None
    }
    fn response_bytes(&self) -> Option<u64> {
        Some(self.sent_total_bytes)
    }
}

#[derive(Serialize, Debug)]
struct AddressLogEntry<'a> {
    ip: &'a str,
    port: u16,
}

#[derive(Serialize, Debug)]
struct HttpRequestLogEntry<'a> {
    protocol: &'a str,
    method: &'a str,
    url: &'a str,
    headers: &'a [(String, String)],
}

#[derive(Serialize, Debug)]
struct HttpResponseLogEntry<'a> {
    status: u32,
    reason: &'a str,
    protocol: &'a str,
    headers: &'a [(String, String)],
}

#[derive(Debug)]
struct LogBook<'a>(&'a [LogEntry]);

#[derive(Serialize, Debug)]
struct LogBookEntry<'a> {
    entry_type: &'a str,
    message: &'a str,
    #[serde(skip_serializing_if="Option::is_none")]
    detail: Option<&'a str>,
}

impl<'a> Serialize for LogBook<'a> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        let mut state = try!(serializer.serialize_seq(Some(self.0.len())));
        for log_entry in self.0 {
            let (entry_type, message, detail) = match log_entry {
                &LogEntry::Vcl(ref msg) => ("VCL", msg.as_str(), None),
                &LogEntry::Debug(ref msg) => ("Debug", msg.as_str(), None),
                &LogEntry::Error(ref msg) => ("Error", msg.as_str(), None),
                &LogEntry::FetchError(ref msg) => ("Fetch Error", msg.as_str(), None),
                &LogEntry::Warning(ref msg) => ("Warning", msg.as_str(), None),
                &LogEntry::Acl(ref result, ref name, ref addr) => match result.as_str() {
                    "MATCH" => ("ACL Match", name.as_str(), addr.as_ref().map(String::as_str)),
                    "NO_MATCH" => ("ACL No Match", name.as_str(), addr.as_ref().map(String::as_str)),
                    _ => ("ACL Other", result.as_str(), Some(name.as_str())),
                },
            };

            try!(serializer.serialize_seq_elt(&mut state, &LogBookEntry {
                entry_type: entry_type,
                message: message,
                detail: detail,
            }));
        }
        try!(serializer.serialize_seq_end(state));
        Ok(())
    }
}

#[derive(Debug)]
struct Index<'a>(&'a LinkedHashMap<String, Vec<String>>);

impl<'a> Serialize for Index<'a> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        let mut state = try!(serializer.serialize_map(Some(self.0.len())));
        for (ref key, ref values) in self.0 {
            try!(serializer.serialize_map_key(&mut state, key));
            try!(serializer.serialize_map_value(&mut state, values));
        }
        try!(serializer.serialize_map_end(state));
        Ok(())
    }
}

#[derive(Serialize, Debug)]
struct CacheObjectLogEntry<'a> {
    storage_type: &'a str,
    storage_name: &'a str,
    ttl_duration: Option<f64>,
    grace_duration: Option<f64>,
    keep_duration: Option<f64>,
    since_timestamp: f64,
    origin_timestamp: f64,
    fetch_mode: &'a str,
    fetch_streamed: bool,
    response: HttpResponseLogEntry<'a>,
}

#[derive(Serialize, Debug)]
struct BackendConnectionLogEntry<'a> {
    fd: isize,
    name: &'a str,
    remote_address: AddressLogEntry<'a>,
    local_address: AddressLogEntry<'a>,
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nickel;
extern crate hyper;
extern crate rustc_serialize;

use nickel::{Nickel, ListeningServer, HttpRouter, JsonBody};
use nickel::status::StatusCode;

use rustc_serialize::json::Json;

use std::error::Error as StdError;
use std::{env, str};
use std::sync::atomic::{self, AtomicUsize};

// Example trait to allow custom databases to be used within the integration tests
// to test different behaviours without touching a real database.
trait Database : Send + Sync + 'static {
    fn get_users(&self) -> Vec<String>;
}

impl Database for Vec<String> {
    fn get_users(&self) -> Vec<String> {
        self.clone()
    }
}

struct ServerData {
    hits: AtomicUsize,

    // FIXME: The `middleware` macro typehinting doesn't support hinting with
    // typeparams, i.e. the hint can't be `< ServerData<T> >` where T is a
    // typeparam from the enclosing function, e.g. `start_server`.
    //
    // To counter this limitation, we've boxed the trait so that specifying the
    // typeparam is unnecessary.
    database: Box<Database>
}

impl ServerData {
    fn hitcount(&self) -> usize {
        self.hits.load(atomic::Ordering::Relaxed)
    }

    fn log_hit(&self) -> usize {
        self.hits.fetch_add(1, atomic::Ordering::Relaxed)
    }

    fn get_users(&self) -> Vec<String> {
        self.database.get_users()
    }
}

fn main() {
    let port = env::var("PORT").map(|s| s.parse().unwrap()).unwrap_or(3000);
    let address = &format!("0.0.0.0:{}", port);
    let database = vec![];

    start_server(address, database).unwrap();
}

fn start_server<D>(address: &str, database: D) -> Result<ListeningServer, Box<StdError>>
where D: Database {
    let server_data = ServerData {
        hits: AtomicUsize::new(0),
        database: Box::new(database),
    };

    let mut server = Nickel::with_data(server_data);

    // Track all hits to the server
    server.utilize(middleware! { |_req, res| <ServerData>
        res.data().log_hit();
        return res.next_middleware()
    });

    // Core server
    server.get("/", middleware!( "Hello World" ));

    server.get("/users", middleware! { |_, res| <ServerData>
        let users = res.data().get_users();

        Json::from_str(&format!(r#"{{ "users": {:?} }}"#, users)).unwrap()
    });

    // Json example
    server.post("/", middleware! { |req, res|
        #[derive(RustcEncodable, RustcDecodable)]
        struct Data { name: String, age: Option<u32> }

        let client = try_with!(res, req.json_as::<Data>().map_err(|e| (StatusCode::BadRequest, e)));

        match client.age {
            Some(age) => {
                Json::from_str(&format!(
                    r#"{{ "message": "Hello {}, your age is {}" }}"#,
                    client.name,
                    age
                )).unwrap()
            }
            None => {
                Json::from_str(&format!(
                    r#"{{ "message": "Hello {}, I don't know your age" }}"#,
                    client.name
                )).unwrap()
            }
        }
    });

    // Get the hitcount
    server.get("/hits", middleware!{ |_req, res| <ServerData>
        res.data().hitcount().to_string()
    });

    server.listen(address)
}

#[cfg(test)]
mod tests {
    use self::support::{Body, Server, STATIC_SERVER, get, post};

    use hyper::header;
    use nickel::status::StatusCode;
    use rustc_serialize::json::Json;

    use std::{thread, time};

    fn get_hits_after_delay(server: &Server) -> u32 {
        // let other tests hit the server
        thread::sleep(time::Duration::from_secs(1));

        let mut response = server.get("/hits");
        response.body().parse().unwrap()
    }

    #[test]
    fn root_responds_with_hello_world() {
        let mut response = get("/");

        assert_eq!(response.body(), "Hello World");
        assert_eq!(response.status, StatusCode::Ok);
    }

    #[test]
    // FIXME: This will probably fail if tests are run without parallelism
    fn server_is_shared_with_other_tests() {
        assert!(get_hits_after_delay(&STATIC_SERVER) > 1);
    }

    #[test]
    fn root_responds_with_modified_json() {
        let mut response = post("/", r#"{ "name": "Rust", "age": 1 }"#);

        let json = Json::from_str(&response.body()).unwrap();

        assert_eq!(json["message"].as_string(), Some("Hello Rust, your age is 1"));
        assert_eq!(response.status, StatusCode::Ok);
        assert_eq!(
            response.headers.get::<header::ContentType>(),
            Some(&header::ContentType::json())
        );
    }

    #[test]
    fn accepts_json_with_missing_fields() {
        let mut response = post("/", r#"{ "name": "Rust" }"#);

        let json = Json::from_str(&response.body()).unwrap();

        assert_eq!(json["message"].as_string(), Some("Hello Rust, I don't know your age"));
        assert_eq!(response.status, StatusCode::Ok);
        assert_eq!(
            response.headers.get::<header::ContentType>(),
            Some(&header::ContentType::json())
        );
    }

    #[test]
    fn doesnt_accept_bad_inputs() {
        let response = post("/", r#"{ }"#);
        assert_eq!(response.status, StatusCode::BadRequest);
    }

    /// This test has a Server instance all to itself.
    #[test]
    fn non_shared_server() {
        let test_local_server = {
            let server = super::start_server("127.0.0.1:0", vec![]).unwrap();
            Server::new(server)
        };

        assert_eq!(get_hits_after_delay(&test_local_server), 1);
    }

    #[test]
    fn has_no_users_by_default() {
        let mut response = get("/users");

        let json = Json::from_str(&response.body()).unwrap();

        assert_eq!(json["users"].as_array().unwrap().len(), 0);
        assert_eq!(response.status, StatusCode::Ok);
        assert_eq!(
            response.headers.get::<header::ContentType>(),
            Some(&header::ContentType::json())
        );
    }

    #[test]
    fn non_shared_server_with_different_database() {
        let server = {
            let bots = vec!["bors".into(), "homu".into(), "highfive".into()];
            let server = super::start_server("127.0.0.1:0", bots).unwrap();
            Server::new(server)
        };

        let mut response = server.get("/users");

        let json = Json::from_str(&response.body()).unwrap();

        assert_eq!(json["users"].as_array().unwrap().len(), 3);
        assert_eq!(response.status, StatusCode::Ok);
        assert_eq!(
            response.headers.get::<header::ContentType>(),
            Some(&header::ContentType::json())
        );
    }

    mod support {
        use hyper::client::{Client, Response as HyperResponse};
        use nickel::ListeningServer;

        use std::net::SocketAddr;

        pub trait Body {
            fn body(self) -> String;
        }

        impl<'a> Body for &'a mut HyperResponse {
            fn body(self) -> String {
                use std::io::Read;
                let mut body = String::new();
                self.read_to_string(&mut body).expect("Failed to read body of Response");
                println!("Read body: {}", body);
                body
            }
        }

        /// An example wrapper type to make testing more readable
        pub struct Server(SocketAddr);
        impl Server {
            pub fn new(server: ListeningServer) -> Server {
                let wrapped = Server(server.socket());

                // detaching is important otherwise it would block the test threads.
                server.detach();

                wrapped
            }

            pub fn get(&self, path: &str) -> HyperResponse {
                let url = self.url_for(path);
                Client::new().get(&url).send().unwrap()
            }

            pub fn post(&self, path: &str, body: &str) -> HyperResponse {
                let url = self.url_for(path);
                Client::new().post(&url).body(body).send().unwrap()
            }

            pub fn url_for(&self, path: &str) -> String {
                format!("http://{}{}", self.0, path)
            }
        }

        lazy_static! {
            /// This is a shared instance of the server between all the tests
            pub static ref STATIC_SERVER: Server = {
                let server = super::super::start_server("127.0.0.1:0", vec![]).unwrap();
                Server::new(server)
            };
        }

        /// Example of a free function version of `get` which uses the shared server
        pub fn get(path: &str) -> HyperResponse {
            STATIC_SERVER.get(path)
        }

        /// Example of a free function version of `post` which uses the shared server
        pub fn post(path: &str, body: &str) -> HyperResponse {
            STATIC_SERVER.post(path, body)
        }
    }
}
<commit_msg>chore(nightly): conditionally include macro_use attribute in integration tests<commit_after>// This attribute being conditional is an implementation detail of the nickel
// test setup for testing examples. Usually, it should just require `#[macro_use]`.
#[cfg_attr(not(test), macro_use)]
extern crate nickel;
extern crate hyper;
extern crate rustc_serialize;

use nickel::{Nickel, ListeningServer, HttpRouter, JsonBody};
use nickel::status::StatusCode;

use rustc_serialize::json::Json;

use std::error::Error as StdError;
use std::{env, str};
use std::sync::atomic::{self, AtomicUsize};

// Example trait to allow custom databases to be used within the integration tests
// to test different behaviours without touching a real database.
trait Database : Send + Sync + 'static {
    fn get_users(&self) -> Vec<String>;
}

impl Database for Vec<String> {
    fn get_users(&self) -> Vec<String> {
        self.clone()
    }
}

struct ServerData {
    hits: AtomicUsize,

    // FIXME: The `middleware` macro typehinting doesn't support hinting with
    // typeparams, i.e. the hint can't be `< ServerData<T> >` where T is a
    // typeparam from the enclosing function, e.g. `start_server`.
    //
    // To counter this limitation, we've boxed the trait so that specifying the
    // typeparam is unnecessary.
    database: Box<Database>
}

impl ServerData {
    fn hitcount(&self) -> usize {
        self.hits.load(atomic::Ordering::Relaxed)
    }

    fn log_hit(&self) -> usize {
        self.hits.fetch_add(1, atomic::Ordering::Relaxed)
    }

    fn get_users(&self) -> Vec<String> {
        self.database.get_users()
    }
}

fn main() {
    let port = env::var("PORT").map(|s| s.parse().unwrap()).unwrap_or(3000);
    let address = &format!("0.0.0.0:{}", port);
    let database = vec![];

    start_server(address, database).unwrap();
}

fn start_server<D>(address: &str, database: D) -> Result<ListeningServer, Box<StdError>>
where D: Database {
    let server_data = ServerData {
        hits: AtomicUsize::new(0),
        database: Box::new(database),
    };

    let mut server = Nickel::with_data(server_data);

    // Track all hits to the server
    server.utilize(middleware! { |_req, res| <ServerData>
        res.data().log_hit();
        return res.next_middleware()
    });

    // Core server
    server.get("/", middleware!( "Hello World" ));

    server.get("/users", middleware! { |_, res| <ServerData>
        let users = res.data().get_users();

        Json::from_str(&format!(r#"{{ "users": {:?} }}"#, users)).unwrap()
    });

    // Json example
    server.post("/", middleware! { |req, res|
        #[derive(RustcEncodable, RustcDecodable)]
        struct Data { name: String, age: Option<u32> }

        let client = try_with!(res, req.json_as::<Data>().map_err(|e| (StatusCode::BadRequest, e)));

        match client.age {
            Some(age) => {
                Json::from_str(&format!(
                    r#"{{ "message": "Hello {}, your age is {}" }}"#,
                    client.name,
                    age
                )).unwrap()
            }
            None => {
                Json::from_str(&format!(
                    r#"{{ "message": "Hello {}, I don't know your age" }}"#,
                    client.name
                )).unwrap()
            }
        }
    });

    // Get the hitcount
    server.get("/hits", middleware!{ |_req, res| <ServerData>
        res.data().hitcount().to_string()
    });

    server.listen(address)
}

#[cfg(test)]
mod tests {
    use self::support::{Body, Server, STATIC_SERVER, get, post};

    use hyper::header;
    use nickel::status::StatusCode;
    use rustc_serialize::json::Json;

    use std::{thread, time};

    fn get_hits_after_delay(server: &Server) -> u32 {
        // let other tests hit the server
        thread::sleep(time::Duration::from_secs(1));

        let mut response = server.get("/hits");
        response.body().parse().unwrap()
    }

    #[test]
    fn root_responds_with_hello_world() {
        let mut response = get("/");

        assert_eq!(response.body(), "Hello World");
        assert_eq!(response.status, StatusCode::Ok);
    }

    #[test]
    // FIXME: This will probably fail if tests are run without parallelism
    fn server_is_shared_with_other_tests() {
        assert!(get_hits_after_delay(&STATIC_SERVER) > 1);
    }

    #[test]
    fn root_responds_with_modified_json() {
        let mut response = post("/", r#"{ "name": "Rust", "age": 1 }"#);

        let json = Json::from_str(&response.body()).unwrap();

        assert_eq!(json["message"].as_string(), Some("Hello Rust, your age is 1"));
        assert_eq!(response.status, StatusCode::Ok);
        assert_eq!(
            response.headers.get::<header::ContentType>(),
            Some(&header::ContentType::json())
        );
    }

    #[test]
    fn accepts_json_with_missing_fields() {
        let mut response = post("/", r#"{ "name": "Rust" }"#);

        let json = Json::from_str(&response.body()).unwrap();

        assert_eq!(json["message"].as_string(), Some("Hello Rust, I don't know your age"));
        assert_eq!(response.status, StatusCode::Ok);
        assert_eq!(
            response.headers.get::<header::ContentType>(),
            Some(&header::ContentType::json())
        );
    }

    #[test]
    fn doesnt_accept_bad_inputs() {
        let response = post("/", r#"{ }"#);
        assert_eq!(response.status, StatusCode::BadRequest);
    }

    /// This test has a Server instance all to itself.
    #[test]
    fn non_shared_server() {
        let test_local_server = {
            let server = super::start_server("127.0.0.1:0", vec![]).unwrap();
            Server::new(server)
        };

        assert_eq!(get_hits_after_delay(&test_local_server), 1);
    }

    #[test]
    fn has_no_users_by_default() {
        let mut response = get("/users");

        let json = Json::from_str(&response.body()).unwrap();

        assert_eq!(json["users"].as_array().unwrap().len(), 0);
        assert_eq!(response.status, StatusCode::Ok);
        assert_eq!(
            response.headers.get::<header::ContentType>(),
            Some(&header::ContentType::json())
        );
    }

    #[test]
    fn non_shared_server_with_different_database() {
        let server = {
            let bots = vec!["bors".into(), "homu".into(), "highfive".into()];
            let server = super::start_server("127.0.0.1:0", bots).unwrap();
            Server::new(server)
        };

        let mut response = server.get("/users");

        let json = Json::from_str(&response.body()).unwrap();

        assert_eq!(json["users"].as_array().unwrap().len(), 3);
        assert_eq!(response.status, StatusCode::Ok);
        assert_eq!(
            response.headers.get::<header::ContentType>(),
            Some(&header::ContentType::json())
        );
    }

    mod support {
        use hyper::client::{Client, Response as HyperResponse};
        use nickel::ListeningServer;

        use std::net::SocketAddr;

        pub trait Body {
            fn body(self) -> String;
        }

        impl<'a> Body for &'a mut HyperResponse {
            fn body(self) -> String {
                use std::io::Read;
                let mut body = String::new();
                self.read_to_string(&mut body).expect("Failed to read body of Response");
                println!("Read body: {}", body);
                body
            }
        }

        /// An example wrapper type to make testing more readable
        pub struct Server(SocketAddr);
        impl Server {
            pub fn new(server: ListeningServer) -> Server {
                let wrapped = Server(server.socket());

                // detaching is important otherwise it would block the test threads.
                server.detach();

                wrapped
            }

            pub fn get(&self, path: &str) -> HyperResponse {
                let url = self.url_for(path);
                Client::new().get(&url).send().unwrap()
            }

            pub fn post(&self, path: &str, body: &str) -> HyperResponse {
                let url = self.url_for(path);
                Client::new().post(&url).body(body).send().unwrap()
            }

            pub fn url_for(&self, path: &str) -> String {
                format!("http://{}{}", self.0, path)
            }
        }

        lazy_static! {
            /// This is a shared instance of the server between all the tests
            pub static ref STATIC_SERVER: Server = {
                let server = super::super::start_server("127.0.0.1:0", vec![]).unwrap();
                Server::new(server)
            };
        }

        /// Example of a free function version of `get` which uses the shared server
        pub fn get(path: &str) -> HyperResponse {
            STATIC_SERVER.get(path)
        }

        /// Example of a free function version of `post` which uses the shared server
        pub fn post(path: &str, body: &str) -> HyperResponse {
            STATIC_SERVER.post(path, body)
        }
    }
}
<|endoftext|>


<commit_before>#[cfg(not(feature = "unstable"))]
mod inner {
    extern crate syntex;
    extern crate diesel_codegen;

    use std::env;
    use std::path::Path;

    pub fn main() {
        let out_dir = env::var_os("OUT_DIR").unwrap();
        let mut registry = syntex::Registry::new();
        diesel_codegen::register(&mut registry);

        let src = Path::new("tests/lib.in.rs");
        let dst = Path::new(&out_dir).join("lib.rs");

        registry.expand("", &src, &dst).unwrap();
    }
}

#[cfg(feature = "unstable")]
mod inner {
    pub fn main() {}
}

fn main() {
    inner::main();
}
<commit_msg>Properly register `dotenv_codegen` on stable<commit_after>#[cfg(not(feature = "unstable"))]
mod inner {
    extern crate syntex;
    extern crate diesel_codegen;
    extern crate dotenv_codegen;

    use std::env;
    use std::path::Path;

    pub fn main() {
        let out_dir = env::var_os("OUT_DIR").unwrap();
        let mut registry = syntex::Registry::new();
        diesel_codegen::register(&mut registry);
        dotenv_codegen::register(&mut registry);

        let src = Path::new("tests/lib.in.rs");
        let dst = Path::new(&out_dir).join("lib.rs");

        registry.expand("", &src, &dst).unwrap();
    }
}

#[cfg(feature = "unstable")]
mod inner {
    pub fn main() {}
}

fn main() {
    inner::main();
}
<|endoftext|>


<commit_before>// Claxon -- A FLAC decoding library in Rust
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

extern crate claxon;
extern crate time;

use claxon::FlacReader;
use std::env;
use std::fs::File;
use std::io::{Cursor, Read};
use std::path::Path;
use time::PreciseTime;

/// Reads a file into memory entirely.
fn read_file<P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut file = File::open(path).unwrap();
    let mut data = Vec::new();
    file.read_to_end(&mut data).unwrap();
    data
}

/// Decode a file into 32-bit integers.
///
/// For every block decoded, appends to the vector the average time spent per
/// sample for that block in nanoseconds.
///
/// Returns a pair of (total time / total samples, total bytes / total time) in
/// units (nanoseconds, bytes per second). Different channels account for
/// different samples; a stereo file of the same sample rate and duration as a
/// mono file will have twice as many samples. Bytes refers to the number of
/// input bytes. (TODO: Do not count header bytes there.)
fn decode_file(data: &[u8], sample_times_ns: &mut Vec<f64>) -> (f64, f64) {
    let cursor = Cursor::new(data);
    let mut reader = FlacReader::new(cursor).unwrap();

    let bps = reader.streaminfo().bits_per_sample as u64;
    let num_samples = reader.streaminfo().samples.unwrap() as i64 * reader.streaminfo().channels as i64;
    assert!(bps < 8 * 16);

    let mut sample_buffer = Vec::new();
    let mut frame_reader = reader.blocks();
    let mut frame_epoch = PreciseTime::now();
    let epoch = frame_epoch;

    loop {
        match frame_reader.read_next_or_eof(sample_buffer) {
            Ok(Some(block)) => {
                // Update timing information.
                let now = PreciseTime::now();
                let duration_ns = frame_epoch.to(now).num_nanoseconds().unwrap();
                let num_samples = block.len() as i64 * block.channels() as i64;
                sample_times_ns.push(duration_ns as f64 / num_samples as f64);
                frame_epoch = now;

                // Recycle the buffer for the next frame. There should be a
                // black box to prevent optimizing this away, but it is only
                // available on unstable Rust, and it seems that nothing is
                // optimized away here anyway. The decode needs to happen anyway
                // to decide whether we hit the panic below.
                sample_buffer = block.into_buffer();
            },
            Ok(None) => break, // End of file.
            Err(_) => panic!("failed to decode")
        }
    }

    let total_duration_ns = epoch.to(PreciseTime::now()).num_nanoseconds().unwrap();
    let ns_per_sample = total_duration_ns as f64 / num_samples as f64;
    let bytes_per_sec = data.len() as f64 * 1000_000_000.0 / total_duration_ns as f64;
    (ns_per_sample, bytes_per_sec)
}

fn print_stats(sample_times_ns: &mut Vec<f64>, stats_pair: (f64, f64)) {
    let (ns_per_sample, bytes_per_sec) = stats_pair;
    sample_times_ns.sort_by(|x, y| x.partial_cmp(y).unwrap());

    let p10 = sample_times_ns[10 * sample_times_ns.len() / 100];
    let p50 = sample_times_ns[50 * sample_times_ns.len() / 100];
    let p90 = sample_times_ns[90 * sample_times_ns.len() / 100];

    println!("{:>6.2} {:>6.2} {:>6.2} {:>6.2} {:>6.2}",
             p10, p50, p90, ns_per_sample, bytes_per_sec / 1024.0 / 1024.0);
}

fn main() {
    let fname = env::args().nth(1).expect("no file given");

    let data = read_file(fname);
    let mut sample_times_ns = Vec::new();

    // Do a few runs to get more robust statistics.
    for _ in 0..5 {
        let stats_pair = decode_file(&data, &mut sample_times_ns);
        print_stats(&mut sample_times_ns, stats_pair);
        sample_times_ns.clear();
    }
}
<commit_msg>Measure throughput more accurately in decode bench<commit_after>// Claxon -- A FLAC decoding library in Rust
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

extern crate claxon;
extern crate time;

use claxon::FlacReader;
use std::env;
use std::fs::File;
use std::io::{Cursor, Read};
use std::path::Path;
use time::PreciseTime;

/// Reads a file into memory entirely.
fn read_file<P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut file = File::open(path).unwrap();
    let mut data = Vec::new();
    file.read_to_end(&mut data).unwrap();
    data
}

/// Decode a file into 32-bit integers.
///
/// For every block decoded, appends to the vector the average time spent per
/// sample for that block in nanoseconds.
///
/// Returns a pair of (total time / total samples, total bytes / total time) in
/// units (nanoseconds, bytes per second). Different channels account for
/// different samples; a stereo file of the same sample rate and duration as a
/// mono file will have twice as many samples. Bytes refers to the number of
/// input bytes. (TODO: Do not count header bytes there.)
fn decode_file(data: &[u8], sample_times_ns: &mut Vec<f64>) -> (f64, f64) {
    // To get the real number of data bytes (excluding metadata -- which can be
    // quite big due to album art), construct a new reader around a cursor, but
    // extract the cursor immediately afterwards. This will read the metadata
    // but not yet any audio data. The position of the cursor is then the amount
    // of metadata bytes.
    let data_bytes = data.len() - FlacReader::new(Cursor::new(data))
        .unwrap()
        .into_inner()
        .position() as usize;

    let cursor = Cursor::new(data);
    let mut reader = FlacReader::new(cursor).unwrap();

    let bps = reader.streaminfo().bits_per_sample as u64;
    let num_samples = reader.streaminfo().samples.unwrap() as i64 * reader.streaminfo().channels as i64;
    assert!(bps < 8 * 16);

    let mut sample_buffer = Vec::new();
    let mut frame_reader = reader.blocks();
    let mut frame_epoch = PreciseTime::now();
    let epoch = frame_epoch;

    loop {
        match frame_reader.read_next_or_eof(sample_buffer) {
            Ok(Some(block)) => {
                // Update timing information.
                let now = PreciseTime::now();
                let duration_ns = frame_epoch.to(now).num_nanoseconds().unwrap();
                let num_samples = block.len() as i64 * block.channels() as i64;
                sample_times_ns.push(duration_ns as f64 / num_samples as f64);
                frame_epoch = now;

                // Recycle the buffer for the next frame. There should be a
                // black box to prevent optimizing this away, but it is only
                // available on unstable Rust, and it seems that nothing is
                // optimized away here anyway. The decode needs to happen anyway
                // to decide whether we hit the panic below.
                sample_buffer = block.into_buffer();
            },
            Ok(None) => break, // End of file.
            Err(_) => panic!("failed to decode")
        }
    }

    let total_duration_ns = epoch.to(PreciseTime::now()).num_nanoseconds().unwrap();
    let ns_per_sample = total_duration_ns as f64 / num_samples as f64;
    let bytes_per_sec = data_bytes as f64 * 1000_000_000.0 / total_duration_ns as f64;
    (ns_per_sample, bytes_per_sec)
}

fn print_stats(sample_times_ns: &mut Vec<f64>, stats_pair: (f64, f64)) {
    let (ns_per_sample, bytes_per_sec) = stats_pair;
    sample_times_ns.sort_by(|x, y| x.partial_cmp(y).unwrap());

    let p10 = sample_times_ns[10 * sample_times_ns.len() / 100];
    let p50 = sample_times_ns[50 * sample_times_ns.len() / 100];
    let p90 = sample_times_ns[90 * sample_times_ns.len() / 100];

    println!("{:>6.2} {:>6.2} {:>6.2} {:>6.2} {:>6.2}",
             p10, p50, p90, ns_per_sample, bytes_per_sec / 1024.0 / 1024.0);
}

fn main() {
    let fname = env::args().nth(1).expect("no file given");

    let data = read_file(fname);
    let mut sample_times_ns = Vec::new();

    // Do a few runs to get more robust statistics.
    for _ in 0..5 {
        let stats_pair = decode_file(&data, &mut sample_times_ns);
        print_stats(&mut sample_times_ns, stats_pair);
        sample_times_ns.clear();
    }
}
<|endoftext|>