<commit_before>
//! Reduce transform to turn pel expressions into rel expressions

use super::rel::*;
use pel;
use typer::FunctionOverload;
use slp_lang_hir as hir;
use std::error;
use std::fmt;

#[derive(PartialEq, Debug, Clone)]
pub enum ReduceError {
    EmptyReduction,
}

pub type ReduceResult<T> = Result<T, ReduceError>;

impl error::Error for ReduceError {
    fn description(&self) -> &str {
        match *self {
            ReduceError::EmptyReduction => "reduced expression is empty",
        }
    }
}

impl fmt::Display for ReduceError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", error::Error::description(self))
    }
}

pub trait ReduceContext {
    fn find_overload(&self, id: &hir::FunctionId) -> FunctionOverload;
}

struct SequenceBuilder {
    binds: Vec<Bind>,
}

impl SequenceBuilder {
    fn new() -> SequenceBuilder {
        SequenceBuilder { binds: vec![] }
    }

    fn push(mut self, bt: BindType, im: InputModifier) -> (SequenceBuilder, BindId) {
        let id = BindId(self.binds.len() as u64);
        self.binds.push(Bind {
            id: id.clone(),
            bind_type: bt,
            required_input: im,
        });
        (self, id)
    }

    fn finalize(mut self) -> ReduceResult<Sequence> {
        match self.binds.pop() {
            Some(last) => {
                Ok(Sequence {
                    binds: self.binds,
                    last: last.bind_type,
                })
            }
            None => Err(ReduceError::EmptyReduction),
        }
    }
}

fn reduce_node(expr: pel::Expression,
               input_modifier: InputModifier,
               sb: SequenceBuilder,
               context: &ReduceContext)
               -> ReduceResult<(SequenceBuilder, BindId)> {
    let (sb, bt) = match expr {
        pel::Expression::Literal(lit) => (sb, BindType::Direct(Command::Literal(lit))),
        pel::Expression::Variable(var) => (sb, BindType::Direct(Command::Variable(var))),
        pel::Expression::Global(id) => (sb, BindType::Direct(Command::Global(id))),
        pel::Expression::ConstantVariable(id, name) => {
            (sb, BindType::Direct(Command::ConstantVariable(id, name)))
        }
        pel::Expression::TernaryConditional(cond, lhs, rhs) => {
            let (sb, cond_id) = try!(reduce_node(*cond, InputModifier::In, sb, context));
            let lhs_seq = Box::new(try!(reduce(*lhs, context)));
            let rhs_seq = Box::new(try!(reduce(*rhs, context)));
            (sb, BindType::Select(cond_id, lhs_seq, rhs_seq))
        }
        pel::Expression::Swizzle(val, swizzle) => {
            let (sb, val_id) = try!(reduce_node(*val, input_modifier.clone(), sb, context));
            (sb, BindType::Direct(Command::Swizzle(val_id, swizzle)))
        }
        pel::Expression::ArraySubscript(arr, index) => {
            let (sb, arr_id) = try!(reduce_node(*arr, InputModifier::In, sb, context));
            let (sb, index_id) = try!(reduce_node(*index, InputModifier::In, sb, context));
            let bt = BindType::Direct(Command::ArraySubscript(arr_id, index_id));
            (sb, bt)
        }
        pel::Expression::Texture2DIndex(dty, tex, index) => {
            let (sb, tex_id) = try!(reduce_node(*tex, InputModifier::In, sb, context));
            let (sb, index_id) = try!(reduce_node(*index, InputModifier::In, sb, context));
            let bt = BindType::Direct(if input_modifier == InputModifier::In {
                Command::Intrinsic2(hir::Intrinsic2::Texture2DLoad(dty), tex_id, index_id)
            } else {
                Command::TextureIndex(dty, tex_id, index_id)
            });
            (sb, bt)
        }
        pel::Expression::RWTexture2DIndex(dty, tex, index) => {
            let (sb, tex_id) = try!(reduce_node(*tex, InputModifier::In, sb, context));
            let (sb, index_id) = try!(reduce_node(*index, InputModifier::In, sb, context));
            let bt = BindType::Direct(if input_modifier == InputModifier::In {
                Command::Intrinsic2(hir::Intrinsic2::RWTexture2DLoad(dty), tex_id, index_id)
            } else {
                Command::TextureIndex(dty, tex_id, index_id)
            });
            (sb, bt)
        }
        pel::Expression::Member(composite, member) => {
            let (sb, composite_id) = try!(reduce_node(*composite,
                                                      input_modifier.clone(),
                                                      sb,
                                                      context));
            (sb, BindType::Direct(Command::Member(composite_id, member)))
        }
        pel::Expression::Call(id, args) => {
            let overload = context.find_overload(&id);
            let fold_fn = |res: ReduceResult<(SequenceBuilder, Vec<BindId>)>, param| {
                let (sb, mut vec) = try!(res);
                let (arg, im) = param;
                let (sb, arg) = try!(reduce_node(arg, im, sb, context));
                vec.push(arg);
                Ok((sb, vec))
            };
            let initial = Vec::with_capacity(args.len());
            let im_iter = overload.2.iter().map(|p| p.1.clone());
            let param_iter = args.into_iter().zip(im_iter);
            let (sb, arg_ids) = try!(param_iter.fold(Ok((sb, initial)), fold_fn));
            (sb, BindType::Direct(Command::Call(id, arg_ids)))
        }
        pel::Expression::NumericConstructor(dtyl, cons) => {
            let fold_fn = |res: ReduceResult<(SequenceBuilder, Vec<ConstructorSlot>)>, arg| {
                let (sb, mut vec) = try!(res);
                let pel::ConstructorSlot { arity, expr } = arg;
                let (sb, arg) = try!(reduce_node(expr, InputModifier::In, sb, context));
                vec.push(ConstructorSlot {
                    arity: arity,
                    expr: arg,
                });
                Ok((sb, vec))
            };
            let initial = Vec::with_capacity(cons.len());
            let (sb, con_ids) = try!(cons.into_iter().fold(Ok((sb, initial)), fold_fn));
            (sb,
             BindType::Direct(Command::NumericConstructor(dtyl, con_ids)))
        }
        pel::Expression::Cast(ty, node) => {
            let (sb, node_id) = try!(reduce_node(*node, input_modifier.clone(), sb, context));
            (sb, BindType::Direct(Command::Cast(ty, node_id)))
        }
        pel::Expression::Intrinsic0(i) => (sb, BindType::Direct(Command::Intrinsic0(i))),
        pel::Expression::Intrinsic1(i, e1) => {
            let im1 = i.get_param1_input_modifier();
            let (sb, e1_id) = try!(reduce_node(*e1, im1, sb, context));
            (sb, BindType::Direct(Command::Intrinsic1(i, e1_id)))
        }
        pel::Expression::Intrinsic2(i, e1, e2) => {
            let im1 = i.get_param1_input_modifier();
            let (sb, e1_id) = try!(reduce_node(*e1, im1, sb, context));
            let im2 = i.get_param2_input_modifier();
            let (sb, e2_id) = try!(reduce_node(*e2, im2, sb, context));
            (sb, BindType::Direct(Command::Intrinsic2(i, e1_id, e2_id)))
        }
        pel::Expression::Intrinsic3(i, e1, e2, e3) => {
            let im1 = i.get_param1_input_modifier();
            let (sb, e1_id) = try!(reduce_node(*e1, im1, sb, context));
            let im2 = i.get_param2_input_modifier();
            let (sb, e2_id) = try!(reduce_node(*e2, im2, sb, context));
            let im3 = i.get_param3_input_modifier();
            let (sb, e3_id) = try!(reduce_node(*e3, im3, sb, context));
            let bt = BindType::Direct(Command::Intrinsic3(i, e1_id, e2_id, e3_id));
            (sb, bt)
        }
    };
    let (sb, id) = sb.push(bt, input_modifier);
    Ok((sb, id))
}

pub fn reduce(root_expr: pel::Expression, context: &ReduceContext) -> ReduceResult<Sequence> {
    let builder = SequenceBuilder::new();
    let builder = try!(reduce_node(root_expr, InputModifier::In, builder, context)).0;
    builder.finalize()
}

#[cfg(test)]
struct TestReduceContext;

#[cfg(test)]
impl ReduceContext for TestReduceContext {
    fn find_overload(&self, _: &hir::FunctionId) -> FunctionOverload {
        panic!("call in tests")
    }
}

#[test]
fn test_reduce_single_variable() {
    let var = hir::VariableRef(hir::VariableId(0), hir::ScopeRef(0));
    let pel = pel::Expression::Variable(var.clone());
    let rel = reduce(pel, &TestReduceContext);
    let expected_rel = Sequence {
        binds: vec![],
        last: BindType::Direct(Command::Variable(var)),
    };
    assert_eq!(rel, Ok(expected_rel));
}

#[test]
fn test_reduce_binary_operation() {
    let var_0_ref = hir::VariableRef(hir::VariableId(0), hir::ScopeRef(0));
    let var_0 = pel::Expression::Variable(var_0_ref.clone());
    let var_1_ref = hir::VariableRef(hir::VariableId(1), hir::ScopeRef(0));
    let var_1 = pel::Expression::Variable(var_1_ref.clone());
    let dty = hir::DataType(hir::DataLayout::Scalar(hir::ScalarType::Float),
                            hir::TypeModifier::default());
    let add = hir::Intrinsic2::Add(dty.clone());
    let pel = pel::Expression::Intrinsic2(add.clone(), Box::new(var_0), Box::new(var_1));
    let rel = reduce(pel, &TestReduceContext);
    let expected_rel = Sequence {
        binds: vec![
            Bind::direct(0, Command::Variable(var_0_ref)),
            Bind::direct(1, Command::Variable(var_1_ref)),
        ],
        last: BindType::Direct(Command::Intrinsic2(add, BindId(0), BindId(1))),
    };
    assert_eq!(rel, Ok(expected_rel));
}
<commit_msg>Add test for rel reduce `texture[] = texture[]`<commit_after>
//! Reduce transform to turn pel expressions into rel expressions

use super::rel::*;
use pel;
use typer::FunctionOverload;
use slp_lang_hir as hir;
use std::error;
use std::fmt;

#[derive(PartialEq, Debug, Clone)]
pub enum ReduceError {
    EmptyReduction,
}

pub type ReduceResult<T> = Result<T, ReduceError>;

impl error::Error for ReduceError {
    fn description(&self) -> &str {
        match *self {
            ReduceError::EmptyReduction => "reduced expression is empty",
        }
    }
}

impl fmt::Display for ReduceError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", error::Error::description(self))
    }
}

pub trait ReduceContext {
    fn find_overload(&self, id: &hir::FunctionId) -> FunctionOverload;
}

struct SequenceBuilder {
    binds: Vec<Bind>,
}

impl SequenceBuilder {
    fn new() -> SequenceBuilder {
        SequenceBuilder { binds: vec![] }
    }

    fn push(mut self, bt: BindType, im: InputModifier) -> (SequenceBuilder, BindId) {
        let id = BindId(self.binds.len() as u64);
        self.binds.push(Bind {
            id: id.clone(),
            bind_type: bt,
            required_input: im,
        });
        (self, id)
    }

    fn finalize(mut self) -> ReduceResult<Sequence> {
        match self.binds.pop() {
            Some(last) => {
                Ok(Sequence {
                    binds: self.binds,
                    last: last.bind_type,
                })
            }
            None => Err(ReduceError::EmptyReduction),
        }
    }
}

fn reduce_node(expr: pel::Expression,
               input_modifier: InputModifier,
               sb: SequenceBuilder,
               context: &ReduceContext)
               -> ReduceResult<(SequenceBuilder, BindId)> {
    let (sb, bt) = match expr {
        pel::Expression::Literal(lit) => (sb, BindType::Direct(Command::Literal(lit))),
        pel::Expression::Variable(var) => (sb, BindType::Direct(Command::Variable(var))),
        pel::Expression::Global(id) => (sb, BindType::Direct(Command::Global(id))),
        pel::Expression::ConstantVariable(id, name) => {
            (sb, BindType::Direct(Command::ConstantVariable(id, name)))
        }
        pel::Expression::TernaryConditional(cond, lhs, rhs) => {
            let (sb, cond_id) = try!(reduce_node(*cond, InputModifier::In, sb, context));
            let lhs_seq = Box::new(try!(reduce(*lhs, context)));
            let rhs_seq = Box::new(try!(reduce(*rhs, context)));
            (sb, BindType::Select(cond_id, lhs_seq, rhs_seq))
        }
        pel::Expression::Swizzle(val, swizzle) => {
            let (sb, val_id) = try!(reduce_node(*val, input_modifier.clone(), sb, context));
            (sb, BindType::Direct(Command::Swizzle(val_id, swizzle)))
        }
        pel::Expression::ArraySubscript(arr, index) => {
            let (sb, arr_id) = try!(reduce_node(*arr, InputModifier::In, sb, context));
            let (sb, index_id) = try!(reduce_node(*index, InputModifier::In, sb, context));
            let bt = BindType::Direct(Command::ArraySubscript(arr_id, index_id));
            (sb, bt)
        }
        pel::Expression::Texture2DIndex(dty, tex, index) => {
            let (sb, tex_id) = try!(reduce_node(*tex, InputModifier::In, sb, context));
            let (sb, index_id) = try!(reduce_node(*index, InputModifier::In, sb, context));
            let bt = BindType::Direct(if input_modifier == InputModifier::In {
                Command::Intrinsic2(hir::Intrinsic2::Texture2DLoad(dty), tex_id, index_id)
            } else {
                Command::TextureIndex(dty, tex_id, index_id)
            });
            (sb, bt)
        }
        pel::Expression::RWTexture2DIndex(dty, tex, index) => {
            let (sb, tex_id) = try!(reduce_node(*tex, InputModifier::In, sb, context));
            let (sb, index_id) = try!(reduce_node(*index, InputModifier::In, sb, context));
            let bt = BindType::Direct(if input_modifier == InputModifier::In {
                Command::Intrinsic2(hir::Intrinsic2::RWTexture2DLoad(dty), tex_id, index_id)
            } else {
                Command::TextureIndex(dty, tex_id, index_id)
            });
            (sb, bt)
        }
        pel::Expression::Member(composite, member) => {
            let (sb, composite_id) = try!(reduce_node(*composite,
                                                      input_modifier.clone(),
                                                      sb,
                                                      context));
            (sb, BindType::Direct(Command::Member(composite_id, member)))
        }
        pel::Expression::Call(id, args) => {
            let overload = context.find_overload(&id);
            let fold_fn = |res: ReduceResult<(SequenceBuilder, Vec<BindId>)>, param| {
                let (sb, mut vec) = try!(res);
                let (arg, im) = param;
                let (sb, arg) = try!(reduce_node(arg, im, sb, context));
                vec.push(arg);
                Ok((sb, vec))
            };
            let initial = Vec::with_capacity(args.len());
            let im_iter = overload.2.iter().map(|p| p.1.clone());
            let param_iter = args.into_iter().zip(im_iter);
            let (sb, arg_ids) = try!(param_iter.fold(Ok((sb, initial)), fold_fn));
            (sb, BindType::Direct(Command::Call(id, arg_ids)))
        }
        pel::Expression::NumericConstructor(dtyl, cons) => {
            let fold_fn = |res: ReduceResult<(SequenceBuilder, Vec<ConstructorSlot>)>, arg| {
                let (sb, mut vec) = try!(res);
                let pel::ConstructorSlot { arity, expr } = arg;
                let (sb, arg) = try!(reduce_node(expr, InputModifier::In, sb, context));
                vec.push(ConstructorSlot {
                    arity: arity,
                    expr: arg,
                });
                Ok((sb, vec))
            };
            let initial = Vec::with_capacity(cons.len());
            let (sb, con_ids) = try!(cons.into_iter().fold(Ok((sb, initial)), fold_fn));
            (sb,
             BindType::Direct(Command::NumericConstructor(dtyl, con_ids)))
        }
        pel::Expression::Cast(ty, node) => {
            let (sb, node_id) = try!(reduce_node(*node, input_modifier.clone(), sb, context));
            (sb, BindType::Direct(Command::Cast(ty, node_id)))
        }
        pel::Expression::Intrinsic0(i) => (sb, BindType::Direct(Command::Intrinsic0(i))),
        pel::Expression::Intrinsic1(i, e1) => {
            let im1 = i.get_param1_input_modifier();
            let (sb, e1_id) = try!(reduce_node(*e1, im1, sb, context));
            (sb, BindType::Direct(Command::Intrinsic1(i, e1_id)))
        }
        pel::Expression::Intrinsic2(i, e1, e2) => {
            let im1 = i.get_param1_input_modifier();
            let (sb, e1_id) = try!(reduce_node(*e1, im1, sb, context));
            let im2 = i.get_param2_input_modifier();
            let (sb, e2_id) = try!(reduce_node(*e2, im2, sb, context));
            (sb, BindType::Direct(Command::Intrinsic2(i, e1_id, e2_id)))
        }
        pel::Expression::Intrinsic3(i, e1, e2, e3) => {
            let im1 = i.get_param1_input_modifier();
            let (sb, e1_id) = try!(reduce_node(*e1, im1, sb, context));
            let im2 = i.get_param2_input_modifier();
            let (sb, e2_id) = try!(reduce_node(*e2, im2, sb, context));
            let im3 = i.get_param3_input_modifier();
            let (sb, e3_id) = try!(reduce_node(*e3, im3, sb, context));
            let bt = BindType::Direct(Command::Intrinsic3(i, e1_id, e2_id, e3_id));
            (sb, bt)
        }
    };
    let (sb, id) = sb.push(bt, input_modifier);
    Ok((sb, id))
}

pub fn reduce(root_expr: pel::Expression, context: &ReduceContext) -> ReduceResult<Sequence> {
    let builder = SequenceBuilder::new();
    let builder = try!(reduce_node(root_expr, InputModifier::In, builder, context)).0;
    builder.finalize()
}

#[cfg(test)]
struct TestReduceContext;

#[cfg(test)]
impl ReduceContext for TestReduceContext {
    fn find_overload(&self, _: &hir::FunctionId) -> FunctionOverload {
        panic!("call in tests")
    }
}

#[test]
fn test_reduce_single_variable() {
    let var = hir::VariableRef(hir::VariableId(0), hir::ScopeRef(0));
    let pel = pel::Expression::Variable(var.clone());
    let rel = reduce(pel, &TestReduceContext);
    let expected_rel = Sequence {
        binds: vec![],
        last: BindType::Direct(Command::Variable(var)),
    };
    assert_eq!(rel, Ok(expected_rel));
}

#[test]
fn test_reduce_binary_operation() {
    let var_0_ref = hir::VariableRef(hir::VariableId(0), hir::ScopeRef(0));
    let var_0 = pel::Expression::Variable(var_0_ref.clone());
    let var_1_ref = hir::VariableRef(hir::VariableId(1), hir::ScopeRef(0));
    let var_1 = pel::Expression::Variable(var_1_ref.clone());
    let dty = hir::DataType(hir::DataLayout::Scalar(hir::ScalarType::Float),
                            hir::TypeModifier::default());
    let add = hir::Intrinsic2::Add(dty.clone());
    let pel = pel::Expression::Intrinsic2(add.clone(), Box::new(var_0), Box::new(var_1));
    let rel = reduce(pel, &TestReduceContext);
    let expected_rel = Sequence {
        binds: vec![
            Bind::direct(0, Command::Variable(var_0_ref)),
            Bind::direct(1, Command::Variable(var_1_ref)),
        ],
        last: BindType::Direct(Command::Intrinsic2(add, BindId(0), BindId(1))),
    };
    assert_eq!(rel, Ok(expected_rel));
}

#[test]
fn test_reduce_texture_assignment() {

    let lit_zero = hir::Literal::Int(0);
    let lit_zero_pel = pel::Expression::Literal(lit_zero.clone());
    let dtyl_index = hir::DataLayout::Vector(hir::ScalarType::Int, 2);
    let cons = vec![
        pel::ConstructorSlot { arity: 1, expr: lit_zero_pel.clone() },
        pel::ConstructorSlot { arity: 1, expr: lit_zero_pel },
    ];
    let lit_zero2 = Box::new(pel::Expression::NumericConstructor(dtyl_index.clone(), cons));

    let dtyl = hir::DataLayout::Vector(hir::ScalarType::Float, 4);
    let dty = hir::DataType(dtyl, hir::TypeModifier::default());
    let tex_0 = hir::GlobalId(0);
    let tex_0_pel = Box::new(pel::Expression::Global(tex_0.clone()));
    let ti_0 = pel::Expression::RWTexture2DIndex(dty.clone(), tex_0_pel, lit_zero2.clone());
    let tex_1 = hir::GlobalId(1);
    let tex_1_pel = Box::new(pel::Expression::Global(tex_1.clone()));
    let ti_1 = pel::Expression::Texture2DIndex(dty.clone(), tex_1_pel, lit_zero2.clone());

    let add = hir::Intrinsic2::Assignment(hir::Type::from_data(dty.clone()));
    let pel = pel::Expression::Intrinsic2(add.clone(), Box::new(ti_0), Box::new(ti_1));

    let rel = reduce(pel, &TestReduceContext);
    let expected_rel = Sequence {
        binds: vec![
            Bind::direct(0, Command::Global(tex_0)),
            Bind::direct(1, Command::Literal(lit_zero.clone())),
            Bind::direct(2, Command::Literal(lit_zero.clone())),
            Bind::direct(3, Command::NumericConstructor(dtyl_index.clone(), vec![ConstructorSlot { arity: 1, expr: BindId(1) }, ConstructorSlot { arity: 1, expr: BindId(2) }])),
            Bind {
                id: BindId(4),
                bind_type: BindType::Direct(Command::TextureIndex(dty.clone(), BindId(0), BindId(3))),
                required_input: InputModifier::InOut,
            },
            Bind::direct(5, Command::Global(tex_1)),
            Bind::direct(6, Command::Literal(lit_zero.clone())),
            Bind::direct(7, Command::Literal(lit_zero.clone())),
            Bind::direct(8, Command::NumericConstructor(dtyl_index, vec![ConstructorSlot { arity: 1, expr: BindId(6) }, ConstructorSlot { arity: 1, expr: BindId(7) }])),
            Bind::direct(9, Command::Intrinsic2(hir::Intrinsic2::Texture2DLoad(dty), BindId(5), BindId(8))),
        ],
        last: BindType::Direct(Command::Intrinsic2(add, BindId(4), BindId(9))),
    };
    assert_eq!(rel, Ok(expected_rel));
}
<|endoftext|>


<commit_before>//! This module is an attempt to provide a friendly, rust-esque interface to Apple's Audio Unit API.
//!
//! Learn more about the Audio Unit API [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003278-CH1-SW2)
//! and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
//!
//! TODO: The following are `kAudioUnitSubType`s (along with their const u32) generated by
//! rust-bindgen that we could not find any documentation on:
//!
//! - MIDISynth            = 1836284270,
//! - RoundTripAAC         = 1918984547,
//! - SpatialMixer         = 862217581,
//! - SphericalHeadPanner  = 1936746610,
//! - VectorPanner         = 1986158963,
//! - SoundFieldPanner     = 1634558569,
//! - HRTFPanner           = 1752331366,
//! - NetReceive           = 1852990326,
//!
//! If you can find documentation on these, please feel free to submit an issue or PR with the
//! fixes!

use bindings::audio_unit as au;
use error::{Error, AudioUnitError};
use libc;
use self::stream_format::StreamFormat;
use std::mem;
use std::ptr;

pub use self::types::{
    Type,
    EffectType,
    FormatConverterType,
    GeneratorType,
    IOType,
    MixerType,
    MusicDeviceType,
};

pub mod audio_format;
pub mod stream_format;
pub mod types;

/// The input and output **Scope**s.
///
/// More info [here](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Scopes)
/// and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
#[derive(Copy, Clone, Debug)]
pub enum Scope {
    Output = 0,
    Input  = 1,
}

/// Represents the **Input** and **Output** **Element**s.
///
/// These are used when specifying which **Element** we're setting the properties of.
#[derive(Copy, Clone, Debug)]
pub enum Element {
    Output = 0,
    Input  = 1,
}

/// The number of frames available in some buffer.
pub type NumFrames = usize;

/// A type representing a render callback (aka "Input Procedure")
/// If set on an AudioUnit, this will be called every time the AudioUnit requests audio.
/// The first arg is [frames[channels]]; the second is the number of frames to render.
pub type RenderCallback = FnMut(&mut[&mut[f32]], NumFrames) -> Result<(), String>;

/// A rust representation of the au::AudioUnit, including a pointer to the current rendering callback.
///
/// Find the original Audio Unit Programming Guide [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
pub struct AudioUnit {
    instance: au::AudioUnit,
    maybe_callback: Option<*mut libc::c_void>
}

macro_rules! try_os_status {
    ($expr:expr) => (try!(Error::from_os_status($expr)))
}

impl AudioUnit {

    /// Construct a new AudioUnit with any type that may be automatically converted into
    /// [**Type**](./enum.Type).
    ///
    /// Here is a list of compatible types:
    ///
    /// - [**Type**](./enum.Type)
    /// - [**IOType**](./enum.IOType)
    /// - [**MusicDeviceType**](./enum.MusicDeviceType)
    /// - [**GeneratorType**](./enum.GeneratorType)
    /// - [**FormatConverterType**](./enum.FormatConverterType)
    /// - [**EffectType**](./enum.EffectType)
    /// - [**MixerType**](./enum.MixerType)
    ///
    /// To construct the **AudioUnit** with some component flags, see
    /// [**AudioUnit::new_with_flags**](./struct.AudioUnit#method.new_with_flags).
    ///
    /// Note: the `AudioUnit` is constructed with the `kAudioUnitManufacturer_Apple` Manufacturer
    /// Identifier, as this is the only Audio Unit Manufacturer Identifier documented by Apple in
    /// the AudioUnit reference (see [here](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AUComponentServicesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Manufacturer_Identifier)).
    pub fn new<T>(ty: T) -> Result<AudioUnit, Error>
        where T: Into<Type>,
    {
        AudioUnit::new_with_flags(ty, 0, 0)
    }

    /// The same as [**AudioUnit::new**](./struct.AudioUnit#method.new) but with the given
    /// component flags and mask.
    pub fn new_with_flags<T>(ty: T, flags: u32, mask: u32) -> Result<AudioUnit, Error>
        where T: Into<Type>,
    {
        const MANUFACTURER_IDENTIFIER: u32 = au::kAudioUnitManufacturer_Apple;
        let au_type: Type = ty.into();
        let sub_type_u32 = match au_type.to_subtype_u32() {
            Some(u) => u,
            None => return Err(Error::NoKnownSubtype),
        };

        // A description of the audio unit we desire.
        let desc = au::AudioComponentDescription {
            componentType: au_type.to_u32() as libc::c_uint,
            componentSubType: sub_type_u32 as libc::c_uint,
            componentManufacturer: MANUFACTURER_IDENTIFIER,
            componentFlags: flags,
            componentFlagsMask: mask,
        };

        unsafe {
            // Find the default audio unit for the description.
            let component = match au::AudioComponentFindNext(ptr::null_mut(), &desc as *const _) {
                component if component.is_null() =>
                    return Err(Error::NoMatchingDefaultAudioUnitFound),
                component => component,
            };

            // Get an instance of the default audio unit using the component.
            let mut instance: au::AudioUnit = mem::uninitialized();
            try_os_status!(
                au::AudioComponentInstanceNew(component, &mut instance as *mut au::AudioUnit)
            );

            // Initialise the audio unit!
            try_os_status!(au::AudioUnitInitialize(instance));
            Ok(AudioUnit {
                instance: instance,
                maybe_callback: None
            })
        }
    }

    /// Sets the value for some property of the **AudioUnit**.
    ///
    /// To clear an audio unit property value, set the data paramater with `None::<()>`.
    ///
    /// Clearing properties only works for those properties that do not have a default value.
    ///
    /// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
    ///
    /// **Available** in iOS 2.0 and later.
    ///
    /// Parameters
    /// ----------
    ///
    /// - **id**: The identifier of the property.
    /// - **scope**: The audio unit scope for the property.
    /// - **elem**: The audio unit element for the property.
    /// - **maybe_data**: The value that you want to apply to the property.
    pub fn set_property<T>(&mut self, id: u32, scope: Scope, elem: Element, maybe_data: Option<&T>)
        -> Result<(), Error>
    {
        let (data_ptr, size) = maybe_data.map(|data| {
            let ptr = data as *const _ as *const libc::c_void;
            let size = ::std::mem::size_of::<T>() as u32;
            (ptr, size)
        }).unwrap_or_else(|| (::std::ptr::null(), 0));
        let scope = scope as libc::c_uint;
        let elem = elem as libc::c_uint;
        unsafe {
            try_os_status!(au::AudioUnitSetProperty(self.instance, id, scope, elem, data_ptr, size))
        }
        Ok(())
    }

    /// Gets the value of an **AudioUnit** property.
    ///
    /// **Available** in iOS 2.0 and later.
    ///
    /// Parameters
    /// ----------
    ///
    /// - **id**: The identifier of the property.
    /// - **scope**: The audio unit scope for the property.
    /// - **elem**: The audio unit element for the property.
    pub fn get_property<T>(&self, id: u32, scope: Scope, elem: Element) -> Result<T, Error> {
        let scope = scope as libc::c_uint;
        let elem = elem as libc::c_uint;
        let mut size = ::std::mem::size_of::<T>() as u32;
        unsafe {
            let mut data: T = ::std::mem::uninitialized();
            let data_ptr = &mut data as *mut _ as *mut libc::c_void;
            let size_ptr = &mut size as *mut _;
            try_os_status!(
                au::AudioUnitGetProperty(self.instance, id, scope, elem, data_ptr, size_ptr)
            );
            Ok(data)
        }
    }

    /// Pass a render callback (aka "Input Procedure") to the **AudioUnit**.
    pub fn set_render_callback(&mut self, f: Option<Box<RenderCallback>>) -> Result<(), Error> {
        // Setup render callback. Notice that we relinquish ownership of the Callback
        // here so that it can be used as the C render callback via a void pointer.
        // We do however store the *mut so that we can convert back to a
        // Box<Box<RenderCallback>> within our AudioUnit's Drop implementation
        // (otherwise it would leak). The double-boxing is due to incompleteness with
        // Rust's FnMut implemetation and is necessary to be able to convert to the
        // correct pointer size.
        let callback_ptr = match f {
            Some(x) => Box::into_raw(Box::new(x)) as *mut libc::c_void,
            _ => ptr::null_mut()
        };
        let render_callback = au::AURenderCallbackStruct {
            inputProc: Some(input_proc),
            inputProcRefCon: callback_ptr
        };

        try!(self.set_property(au::kAudioUnitProperty_SetRenderCallback,
                               Scope::Input,
                               Element::Output,
                               Some(&render_callback)));

        self.free_render_callback();
        self.maybe_callback = if !callback_ptr.is_null() { Some(callback_ptr) } else { None };
        Ok(())
    }

    /// Retrieves ownership over the render callback and drops it.
    fn free_render_callback(&mut self) {
        if let Some(callback) = self.maybe_callback.take() {
            // Here, we transfer ownership of the callback back to the current scope so that it
            // is dropped and cleaned up. Without this line, we would leak the Boxed callback.
            let _: Box<Box<RenderCallback>> = unsafe {
                Box::from_raw(callback as *mut Box<RenderCallback>)
            };
        }
    }

    /// Starts an I/O **AudioUnit**, which in turn starts the audio unit processing graph that it is
    /// connected to.
    ///
    /// **Available** in OS X v10.0 and later.
    pub fn start(&mut self) -> Result<(), Error> {
        unsafe { try_os_status!(au::AudioOutputUnitStart(self.instance)); }
        Ok(())
    }

    /// Stops an I/O **AudioUnit**, which in turn stops the audio unit processing graph that it is
    /// connected to.
    ///
    /// **Available** in OS X v10.0 and later.
    pub fn stop(&mut self) -> Result<(), Error> {
        unsafe { try_os_status!(au::AudioOutputUnitStop(self.instance)); }
        Ok(())
    }

    /// Set the **AudioUnit**'s sample rate.
    ///
    /// **Available** in iOS 2.0 and later.
    pub fn set_sample_rate(&mut self, sample_rate: f64) -> Result<(), Error> {
        let id = au::kAudioUnitProperty_SampleRate;
        self.set_property(id, Scope::Input, Element::Output, Some(&sample_rate))
    }

    /// Get the **AudioUnit**'s sample rate.
    pub fn sample_rate(&self) -> Result<f64, Error> {
        let id = au::kAudioUnitProperty_SampleRate;
        self.get_property(id, Scope::Input, Element::Output)
    }

    /// Sets the current **StreamFormat** for the AudioUnit.
    pub fn set_stream_format(&mut self, stream_format: StreamFormat) -> Result<(), Error> {
        let id = au::kAudioUnitProperty_StreamFormat;
        let asbd = stream_format.to_asbd();
        self.set_property(id, Scope::Input, Element::Output, Some(&asbd))
    }

    /// Return the current Stream Format for the AudioUnit.
    pub fn stream_format(&self) -> Result<StreamFormat, Error> {
        let id = au::kAudioUnitProperty_StreamFormat;
        let asbd = try!(self.get_property(id, Scope::Output, Element::Output));
        StreamFormat::from_asbd(asbd)
    }

}

impl Drop for AudioUnit {
    fn drop(&mut self) {
        unsafe {
            use error;
            use std::error::Error;
            if let Err(err) = self.stop() {
                panic!("{:?}", err.description());
            }
            if let Err(err) = error::Error::from_os_status(au::AudioUnitUninitialize(self.instance)) {
                panic!("{:?}", err.description());
            }
            self.free_render_callback();
        }
    }
}

/// Callback procedure that will be called each time our audio_unit requests audio.
extern "C" fn input_proc(in_ref_con: *mut libc::c_void,
                         _io_action_flags: *mut au::AudioUnitRenderActionFlags,
                         _in_time_stamp: *const au::AudioTimeStamp,
                         _in_bus_number: au::UInt32,
                         in_number_frames: au::UInt32,
                         io_data: *mut au::AudioBufferList) -> au::OSStatus {
    let callback: *mut Box<RenderCallback> = in_ref_con as *mut _;
    unsafe {
        let num_channels = (*io_data).mNumberBuffers as usize;

        // FIXME: We shouldn't need a Vec for this, it should probably be something like
        // `&[&mut [f32]]` instead.
        let mut channels: Vec<&mut [f32]> =
            (0..num_channels)
                .map(|i| {
                    let slice_ptr = (*io_data).mBuffers[i].mData as *mut libc::c_float;
                    // TODO: the size of this buffer needs to be calculated properly based on the stream format.
                    // Currently this won't be correct in at least this case:
                    /*
                    stream_format::StreamFormat {
                        sample_rate: 44100.0,
                        audio_format: audio_format::AudioFormat::LinearPCM(Some(audio_format::LinearPCMFlag::IsFloat)),
                        bytes_per_packet: 2 * 4,
                        frames_per_packet: 1,
                        bytes_per_frame: 2 * 4,
                        channels_per_frame: 2,
                        bits_per_channel: 32
                    }
                     */
                    ::std::slice::from_raw_parts_mut(slice_ptr, in_number_frames as usize)
                })
                .collect();

        match (*callback)(&mut channels[..], in_number_frames as usize) {
            Ok(()) => 0 as au::OSStatus,
            Err(description) => {
                use std::io::Write;
                writeln!(::std::io::stderr(), "{:?}", description).unwrap();
                AudioUnitError::NoConnection as au::OSStatus
            },
        }
    }
}
<commit_msg>Fix subtype links in new method docs<commit_after>//! This module is an attempt to provide a friendly, rust-esque interface to Apple's Audio Unit API.
//!
//! Learn more about the Audio Unit API [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003278-CH1-SW2)
//! and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
//!
//! TODO: The following are `kAudioUnitSubType`s (along with their const u32) generated by
//! rust-bindgen that we could not find any documentation on:
//!
//! - MIDISynth            = 1836284270,
//! - RoundTripAAC         = 1918984547,
//! - SpatialMixer         = 862217581,
//! - SphericalHeadPanner  = 1936746610,
//! - VectorPanner         = 1986158963,
//! - SoundFieldPanner     = 1634558569,
//! - HRTFPanner           = 1752331366,
//! - NetReceive           = 1852990326,
//!
//! If you can find documentation on these, please feel free to submit an issue or PR with the
//! fixes!

use bindings::audio_unit as au;
use error::{Error, AudioUnitError};
use libc;
use self::stream_format::StreamFormat;
use std::mem;
use std::ptr;

pub use self::types::{
    Type,
    EffectType,
    FormatConverterType,
    GeneratorType,
    IOType,
    MixerType,
    MusicDeviceType,
};

pub mod audio_format;
pub mod stream_format;
pub mod types;

/// The input and output **Scope**s.
///
/// More info [here](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Scopes)
/// and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
#[derive(Copy, Clone, Debug)]
pub enum Scope {
    Output = 0,
    Input  = 1,
}

/// Represents the **Input** and **Output** **Element**s.
///
/// These are used when specifying which **Element** we're setting the properties of.
#[derive(Copy, Clone, Debug)]
pub enum Element {
    Output = 0,
    Input  = 1,
}

/// The number of frames available in some buffer.
pub type NumFrames = usize;

/// A type representing a render callback (aka "Input Procedure")
/// If set on an AudioUnit, this will be called every time the AudioUnit requests audio.
/// The first arg is [frames[channels]]; the second is the number of frames to render.
pub type RenderCallback = FnMut(&mut[&mut[f32]], NumFrames) -> Result<(), String>;

/// A rust representation of the au::AudioUnit, including a pointer to the current rendering callback.
///
/// Find the original Audio Unit Programming Guide [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
pub struct AudioUnit {
    instance: au::AudioUnit,
    maybe_callback: Option<*mut libc::c_void>
}

macro_rules! try_os_status {
    ($expr:expr) => (try!(Error::from_os_status($expr)))
}

impl AudioUnit {

    /// Construct a new AudioUnit with any type that may be automatically converted into
    /// [**Type**](./enum.Type).
    ///
    /// Here is a list of compatible types:
    ///
    /// - [**Type**](./types/enum.Type)
    /// - [**IOType**](./types/enum.IOType)
    /// - [**MusicDeviceType**](./types/enum.MusicDeviceType)
    /// - [**GeneratorType**](./types/enum.GeneratorType)
    /// - [**FormatConverterType**](./types/enum.FormatConverterType)
    /// - [**EffectType**](./types/enum.EffectType)
    /// - [**MixerType**](./types/enum.MixerType)
    ///
    /// To construct the **AudioUnit** with some component flags, see
    /// [**AudioUnit::new_with_flags**](./struct.AudioUnit#method.new_with_flags).
    ///
    /// Note: the `AudioUnit` is constructed with the `kAudioUnitManufacturer_Apple` Manufacturer
    /// Identifier, as this is the only Audio Unit Manufacturer Identifier documented by Apple in
    /// the AudioUnit reference (see [here](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AUComponentServicesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Manufacturer_Identifier)).
    pub fn new<T>(ty: T) -> Result<AudioUnit, Error>
        where T: Into<Type>,
    {
        AudioUnit::new_with_flags(ty, 0, 0)
    }

    /// The same as [**AudioUnit::new**](./struct.AudioUnit#method.new) but with the given
    /// component flags and mask.
    pub fn new_with_flags<T>(ty: T, flags: u32, mask: u32) -> Result<AudioUnit, Error>
        where T: Into<Type>,
    {
        const MANUFACTURER_IDENTIFIER: u32 = au::kAudioUnitManufacturer_Apple;
        let au_type: Type = ty.into();
        let sub_type_u32 = match au_type.to_subtype_u32() {
            Some(u) => u,
            None => return Err(Error::NoKnownSubtype),
        };

        // A description of the audio unit we desire.
        let desc = au::AudioComponentDescription {
            componentType: au_type.to_u32() as libc::c_uint,
            componentSubType: sub_type_u32 as libc::c_uint,
            componentManufacturer: MANUFACTURER_IDENTIFIER,
            componentFlags: flags,
            componentFlagsMask: mask,
        };

        unsafe {
            // Find the default audio unit for the description.
            let component = match au::AudioComponentFindNext(ptr::null_mut(), &desc as *const _) {
                component if component.is_null() =>
                    return Err(Error::NoMatchingDefaultAudioUnitFound),
                component => component,
            };

            // Get an instance of the default audio unit using the component.
            let mut instance: au::AudioUnit = mem::uninitialized();
            try_os_status!(
                au::AudioComponentInstanceNew(component, &mut instance as *mut au::AudioUnit)
            );

            // Initialise the audio unit!
            try_os_status!(au::AudioUnitInitialize(instance));
            Ok(AudioUnit {
                instance: instance,
                maybe_callback: None
            })
        }
    }

    /// Sets the value for some property of the **AudioUnit**.
    ///
    /// To clear an audio unit property value, set the data paramater with `None::<()>`.
    ///
    /// Clearing properties only works for those properties that do not have a default value.
    ///
    /// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
    ///
    /// **Available** in iOS 2.0 and later.
    ///
    /// Parameters
    /// ----------
    ///
    /// - **id**: The identifier of the property.
    /// - **scope**: The audio unit scope for the property.
    /// - **elem**: The audio unit element for the property.
    /// - **maybe_data**: The value that you want to apply to the property.
    pub fn set_property<T>(&mut self, id: u32, scope: Scope, elem: Element, maybe_data: Option<&T>)
        -> Result<(), Error>
    {
        let (data_ptr, size) = maybe_data.map(|data| {
            let ptr = data as *const _ as *const libc::c_void;
            let size = ::std::mem::size_of::<T>() as u32;
            (ptr, size)
        }).unwrap_or_else(|| (::std::ptr::null(), 0));
        let scope = scope as libc::c_uint;
        let elem = elem as libc::c_uint;
        unsafe {
            try_os_status!(au::AudioUnitSetProperty(self.instance, id, scope, elem, data_ptr, size))
        }
        Ok(())
    }

    /// Gets the value of an **AudioUnit** property.
    ///
    /// **Available** in iOS 2.0 and later.
    ///
    /// Parameters
    /// ----------
    ///
    /// - **id**: The identifier of the property.
    /// - **scope**: The audio unit scope for the property.
    /// - **elem**: The audio unit element for the property.
    pub fn get_property<T>(&self, id: u32, scope: Scope, elem: Element) -> Result<T, Error> {
        let scope = scope as libc::c_uint;
        let elem = elem as libc::c_uint;
        let mut size = ::std::mem::size_of::<T>() as u32;
        unsafe {
            let mut data: T = ::std::mem::uninitialized();
            let data_ptr = &mut data as *mut _ as *mut libc::c_void;
            let size_ptr = &mut size as *mut _;
            try_os_status!(
                au::AudioUnitGetProperty(self.instance, id, scope, elem, data_ptr, size_ptr)
            );
            Ok(data)
        }
    }

    /// Pass a render callback (aka "Input Procedure") to the **AudioUnit**.
    pub fn set_render_callback(&mut self, f: Option<Box<RenderCallback>>) -> Result<(), Error> {
        // Setup render callback. Notice that we relinquish ownership of the Callback
        // here so that it can be used as the C render callback via a void pointer.
        // We do however store the *mut so that we can convert back to a
        // Box<Box<RenderCallback>> within our AudioUnit's Drop implementation
        // (otherwise it would leak). The double-boxing is due to incompleteness with
        // Rust's FnMut implemetation and is necessary to be able to convert to the
        // correct pointer size.
        let callback_ptr = match f {
            Some(x) => Box::into_raw(Box::new(x)) as *mut libc::c_void,
            _ => ptr::null_mut()
        };
        let render_callback = au::AURenderCallbackStruct {
            inputProc: Some(input_proc),
            inputProcRefCon: callback_ptr
        };

        try!(self.set_property(au::kAudioUnitProperty_SetRenderCallback,
                               Scope::Input,
                               Element::Output,
                               Some(&render_callback)));

        self.free_render_callback();
        self.maybe_callback = if !callback_ptr.is_null() { Some(callback_ptr) } else { None };
        Ok(())
    }

    /// Retrieves ownership over the render callback and drops it.
    fn free_render_callback(&mut self) {
        if let Some(callback) = self.maybe_callback.take() {
            // Here, we transfer ownership of the callback back to the current scope so that it
            // is dropped and cleaned up. Without this line, we would leak the Boxed callback.
            let _: Box<Box<RenderCallback>> = unsafe {
                Box::from_raw(callback as *mut Box<RenderCallback>)
            };
        }
    }

    /// Starts an I/O **AudioUnit**, which in turn starts the audio unit processing graph that it is
    /// connected to.
    ///
    /// **Available** in OS X v10.0 and later.
    pub fn start(&mut self) -> Result<(), Error> {
        unsafe { try_os_status!(au::AudioOutputUnitStart(self.instance)); }
        Ok(())
    }

    /// Stops an I/O **AudioUnit**, which in turn stops the audio unit processing graph that it is
    /// connected to.
    ///
    /// **Available** in OS X v10.0 and later.
    pub fn stop(&mut self) -> Result<(), Error> {
        unsafe { try_os_status!(au::AudioOutputUnitStop(self.instance)); }
        Ok(())
    }

    /// Set the **AudioUnit**'s sample rate.
    ///
    /// **Available** in iOS 2.0 and later.
    pub fn set_sample_rate(&mut self, sample_rate: f64) -> Result<(), Error> {
        let id = au::kAudioUnitProperty_SampleRate;
        self.set_property(id, Scope::Input, Element::Output, Some(&sample_rate))
    }

    /// Get the **AudioUnit**'s sample rate.
    pub fn sample_rate(&self) -> Result<f64, Error> {
        let id = au::kAudioUnitProperty_SampleRate;
        self.get_property(id, Scope::Input, Element::Output)
    }

    /// Sets the current **StreamFormat** for the AudioUnit.
    pub fn set_stream_format(&mut self, stream_format: StreamFormat) -> Result<(), Error> {
        let id = au::kAudioUnitProperty_StreamFormat;
        let asbd = stream_format.to_asbd();
        self.set_property(id, Scope::Input, Element::Output, Some(&asbd))
    }

    /// Return the current Stream Format for the AudioUnit.
    pub fn stream_format(&self) -> Result<StreamFormat, Error> {
        let id = au::kAudioUnitProperty_StreamFormat;
        let asbd = try!(self.get_property(id, Scope::Output, Element::Output));
        StreamFormat::from_asbd(asbd)
    }

}

impl Drop for AudioUnit {
    fn drop(&mut self) {
        unsafe {
            use error;
            use std::error::Error;
            if let Err(err) = self.stop() {
                panic!("{:?}", err.description());
            }
            if let Err(err) = error::Error::from_os_status(au::AudioUnitUninitialize(self.instance)) {
                panic!("{:?}", err.description());
            }
            self.free_render_callback();
        }
    }
}

/// Callback procedure that will be called each time our audio_unit requests audio.
extern "C" fn input_proc(in_ref_con: *mut libc::c_void,
                         _io_action_flags: *mut au::AudioUnitRenderActionFlags,
                         _in_time_stamp: *const au::AudioTimeStamp,
                         _in_bus_number: au::UInt32,
                         in_number_frames: au::UInt32,
                         io_data: *mut au::AudioBufferList) -> au::OSStatus {
    let callback: *mut Box<RenderCallback> = in_ref_con as *mut _;
    unsafe {
        let num_channels = (*io_data).mNumberBuffers as usize;

        // FIXME: We shouldn't need a Vec for this, it should probably be something like
        // `&[&mut [f32]]` instead.
        let mut channels: Vec<&mut [f32]> =
            (0..num_channels)
                .map(|i| {
                    let slice_ptr = (*io_data).mBuffers[i].mData as *mut libc::c_float;
                    // TODO: the size of this buffer needs to be calculated properly based on the stream format.
                    // Currently this won't be correct in at least this case:
                    /*
                    stream_format::StreamFormat {
                        sample_rate: 44100.0,
                        audio_format: audio_format::AudioFormat::LinearPCM(Some(audio_format::LinearPCMFlag::IsFloat)),
                        bytes_per_packet: 2 * 4,
                        frames_per_packet: 1,
                        bytes_per_frame: 2 * 4,
                        channels_per_frame: 2,
                        bits_per_channel: 32
                    }
                     */
                    ::std::slice::from_raw_parts_mut(slice_ptr, in_number_frames as usize)
                })
                .collect();

        match (*callback)(&mut channels[..], in_number_frames as usize) {
            Ok(()) => 0 as au::OSStatus,
            Err(description) => {
                use std::io::Write;
                writeln!(::std::io::stderr(), "{:?}", description).unwrap();
                AudioUnitError::NoConnection as au::OSStatus
            },
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module implements structured cloning, as defined by [HTML]
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).

use dom::bindings::conversions::root_from_handleobject;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::js::Root;
use dom::bindings::reflector::DomObject;
use dom::blob::{Blob, BlobImpl};
use dom::globalscope::GlobalScope;
use js::jsapi::{Handle, HandleObject, HandleValue, MutableHandleValue, JSAutoCompartment, JSContext};
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
use js::jsapi::{JS_ClearPendingException, JSObject, JS_ReadStructuredClone};
use js::jsapi::{JS_ReadBytes, JS_WriteBytes};
use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair};
use js::jsapi::{JS_STRUCTURED_CLONE_VERSION, JS_WriteStructuredClone};
use js::jsapi::{MutableHandleObject, TransferableOwnership};
use libc::size_t;
use std::os::raw;
use std::ptr;
use std::slice;

// TODO: Should we add Min and Max const to https://github.com/servo/rust-mozjs/blob/master/src/consts.rs?
// TODO: Determine for sure which value Min and Max should have.
// NOTE: Current values found at https://dxr.mozilla.org/mozilla-central/
// rev/ff04d410e74b69acfab17ef7e73e7397602d5a68/js/public/StructuredClone.h#323
#[repr(u32)]
enum StructuredCloneTags {
    /// To support additional types, add new tags with values incremented from the last one before Max.
    Min = 0xFFFF8000,
    DomBlob = 0xFFFF8001,
    Max = 0xFFFFFFFF,
}

#[cfg(target_pointer_width = "64")]
unsafe fn write_length(w: *mut JSStructuredCloneWriter,
                       length: usize) {
  let high: u32 = (length >> 32) as u32;
  let low: u32 = length as u32;
  assert!(JS_WriteUint32Pair(w, high, low));
}

#[cfg(target_pointer_width = "32")]
unsafe fn write_length(w: *mut JSStructuredCloneWriter,
                       length: usize) {
  assert!(JS_WriteUint32Pair(w, length as u32, 0));
}

#[cfg(target_pointer_width = "64")]
unsafe fn read_length(r: *mut JSStructuredCloneReader)
                      -> usize {
  let mut high: u32 = 0;
  let mut low: u32 = 0;
  assert!(JS_ReadUint32Pair(r, &mut high as *mut u32, &mut low as *mut u32));
  return (low << high) as usize;
}

#[cfg(target_pointer_width = "32")]
unsafe fn read_length(r: *mut JSStructuredCloneReader)
                      -> usize {
  let mut length: u32 = 0;
  let mut zero: u32 = 0;
  assert!(JS_ReadUint32Pair(r, &mut length as *mut u32, &mut zero as *mut u32));
  return length as usize;
}

unsafe fn read_blob(cx: *mut JSContext,
                    r: *mut JSStructuredCloneReader)
                    -> *mut JSObject {
    let blob_length = read_length(r);
    let type_str_length = read_length(r);
    let mut blob_buffer = vec![0u8; blob_length];
    assert!(JS_ReadBytes(r, blob_buffer.as_mut_ptr() as *mut raw::c_void, blob_length));
    let mut type_str_buffer = vec![0u8; type_str_length];
    assert!(JS_ReadBytes(r, type_str_buffer.as_mut_ptr() as *mut raw::c_void, type_str_length));
    let type_str = String::from_utf8_unchecked(type_str_buffer);
    let target_global = GlobalScope::from_context(cx);
    let blob = Blob::new(&target_global, BlobImpl::new_from_bytes(blob_buffer), type_str);
    return blob.reflector().get_jsobject().get()
}

unsafe fn write_blob(blob: Root<Blob>,
                     w: *mut JSStructuredCloneWriter)
                     -> Result<(), ()> {
    let blob_vec = try!(blob.get_bytes());
    let blob_length = blob_vec.len();
    let type_string_bytes = blob.type_string().as_bytes().to_vec();
    let type_string_length = type_string_bytes.len();
    assert!(JS_WriteUint32Pair(w, StructuredCloneTags::DomBlob as u32, 0));
    write_length(w, blob_length);
    write_length(w, type_string_length);
    assert!(JS_WriteBytes(w, blob_vec.as_ptr() as *const raw::c_void, blob_length));
    assert!(JS_WriteBytes(w, type_string_bytes.as_ptr() as *const raw::c_void, type_string_length));
    return Ok(())
}

unsafe extern "C" fn read_callback(cx: *mut JSContext,
                                   r: *mut JSStructuredCloneReader,
                                   tag: u32,
                                   _data: u32,
                                   _closure: *mut raw::c_void)
                                   -> *mut JSObject {
    assert!(tag < StructuredCloneTags::Max as u32, "tag should be lower than StructuredCloneTags::Max");
    assert!(tag > StructuredCloneTags::Min as u32, "tag should be higher than StructuredCloneTags::Min");
    if tag == StructuredCloneTags::DomBlob as u32 {
        return read_blob(cx, r)
    }
    return ptr::null_mut()
}

unsafe extern "C" fn write_callback(_cx: *mut JSContext,
                                    w: *mut JSStructuredCloneWriter,
                                    obj: HandleObject,
                                    _closure: *mut raw::c_void)
                                    -> bool {
    if let Ok(blob) = root_from_handleobject::<Blob>(obj) {
        return write_blob(blob, w).is_ok()
    }
    return false
}

unsafe extern "C" fn read_transfer_callback(_cx: *mut JSContext,
                                            _r: *mut JSStructuredCloneReader,
                                            _tag: u32,
                                            _content: *mut raw::c_void,
                                            _extra_data: u64,
                                            _closure: *mut raw::c_void,
                                            _return_object: MutableHandleObject)
                                            -> bool {
    false
}

unsafe extern "C" fn write_transfer_callback(_cx: *mut JSContext,
                                             _obj: Handle<*mut JSObject>,
                                             _closure: *mut raw::c_void,
                                             _tag: *mut u32,
                                             _ownership: *mut TransferableOwnership,
                                             _content:  *mut *mut raw::c_void,
                                             _extra_data: *mut u64)
                                             -> bool {
    false
}

unsafe extern "C" fn free_transfer_callback(_tag: u32,
                                            _ownership: TransferableOwnership,
                                            _content: *mut raw::c_void,
                                            _extra_data: u64,
                                            _closure: *mut raw::c_void) {
}

unsafe extern "C" fn report_error_callback(_cx: *mut JSContext, _errorid: u32) {
}

static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredCloneCallbacks {
    read: Some(read_callback),
    write: Some(write_callback),
    reportError: Some(report_error_callback),
    readTransfer: Some(read_transfer_callback),
    writeTransfer: Some(write_transfer_callback),
    freeTransfer: Some(free_transfer_callback),
};

/// A buffer for a structured clone.
pub enum StructuredCloneData {
    /// A non-serializable (default) variant
    Struct(*mut u64, size_t),
    /// A variant that can be serialized
    Vector(Vec<u8>)
}

impl StructuredCloneData {
    /// Writes a structured clone. Returns a `DataClone` error if that fails.
    pub fn write(cx: *mut JSContext, message: HandleValue) -> Fallible<StructuredCloneData> {
        let mut data = ptr::null_mut();
        let mut nbytes = 0;
        let result = unsafe {
            JS_WriteStructuredClone(cx,
                                    message,
                                    &mut data,
                                    &mut nbytes,
                                    &STRUCTURED_CLONE_CALLBACKS,
                                    ptr::null_mut(),
                                    HandleValue::undefined())
        };
        if !result {
            unsafe {
                JS_ClearPendingException(cx);
            }
            return Err(Error::DataClone);
        }
        Ok(StructuredCloneData::Struct(data, nbytes))
    }

    /// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing
    pub fn move_to_arraybuffer(self) -> Vec<u8> {
        match self {
            StructuredCloneData::Struct(data, nbytes) => {
                unsafe {
                    slice::from_raw_parts(data as *mut u8, nbytes).to_vec()
                }
            }
            StructuredCloneData::Vector(msg) => msg
        }
    }

    /// Reads a structured clone.
    ///
    /// Panics if `JS_ReadStructuredClone` fails.
    fn read_clone(global: &GlobalScope,
                  data: *mut u64,
                  nbytes: size_t,
                  rval: MutableHandleValue) {
        let cx = global.get_cx();
        let globalhandle = global.reflector().get_jsobject();
        let _ac = JSAutoCompartment::new(cx, globalhandle.get());
        unsafe {
            assert!(JS_ReadStructuredClone(cx,
                                           data,
                                           nbytes,
                                           JS_STRUCTURED_CLONE_VERSION,
                                           rval,
                                           &STRUCTURED_CLONE_CALLBACKS,
                                           ptr::null_mut()));
        }
    }

    /// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
    pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) {
        match self {
            StructuredCloneData::Vector(mut vec_msg) => {
                let nbytes = vec_msg.len();
                let data = vec_msg.as_mut_ptr() as *mut u64;
                StructuredCloneData::read_clone(global, data, nbytes, rval);
            }
            StructuredCloneData::Struct(data, nbytes) => StructuredCloneData::read_clone(global, data, nbytes, rval)
        }
    }
}

unsafe impl Send for StructuredCloneData {}
<commit_msg>Auto merge of #16884 - jdm:structuredclone, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module implements structured cloning, as defined by [HTML]
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).

use dom::bindings::conversions::root_from_handleobject;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::js::Root;
use dom::bindings::reflector::DomObject;
use dom::blob::{Blob, BlobImpl};
use dom::globalscope::GlobalScope;
use js::jsapi::{Handle, HandleObject, HandleValue, MutableHandleValue, JSAutoCompartment, JSContext};
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
use js::jsapi::{JS_ClearPendingException, JSObject, JS_ReadStructuredClone};
use js::jsapi::{JS_ReadBytes, JS_WriteBytes};
use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair};
use js::jsapi::{JS_STRUCTURED_CLONE_VERSION, JS_WriteStructuredClone};
use js::jsapi::{MutableHandleObject, TransferableOwnership};
use libc::size_t;
use std::os::raw;
use std::ptr;
use std::slice;

// TODO: Should we add Min and Max const to https://github.com/servo/rust-mozjs/blob/master/src/consts.rs?
// TODO: Determine for sure which value Min and Max should have.
// NOTE: Current values found at https://dxr.mozilla.org/mozilla-central/
// rev/ff04d410e74b69acfab17ef7e73e7397602d5a68/js/public/StructuredClone.h#323
#[repr(u32)]
enum StructuredCloneTags {
    /// To support additional types, add new tags with values incremented from the last one before Max.
    Min = 0xFFFF8000,
    DomBlob = 0xFFFF8001,
    Max = 0xFFFFFFFF,
}

#[cfg(target_pointer_width = "64")]
unsafe fn write_length(w: *mut JSStructuredCloneWriter,
                       length: usize) {
  let high: u32 = (length >> 32) as u32;
  let low: u32 = length as u32;
  assert!(JS_WriteUint32Pair(w, high, low));
}

#[cfg(target_pointer_width = "32")]
unsafe fn write_length(w: *mut JSStructuredCloneWriter,
                       length: usize) {
  assert!(JS_WriteUint32Pair(w, length as u32, 0));
}

#[cfg(target_pointer_width = "64")]
unsafe fn read_length(r: *mut JSStructuredCloneReader)
                      -> usize {
  let mut high: u32 = 0;
  let mut low: u32 = 0;
  assert!(JS_ReadUint32Pair(r, &mut high as *mut u32, &mut low as *mut u32));
  return (low << high) as usize;
}

#[cfg(target_pointer_width = "32")]
unsafe fn read_length(r: *mut JSStructuredCloneReader)
                      -> usize {
  let mut length: u32 = 0;
  let mut zero: u32 = 0;
  assert!(JS_ReadUint32Pair(r, &mut length as *mut u32, &mut zero as *mut u32));
  return length as usize;
}

struct StructuredCloneWriter {
    w: *mut JSStructuredCloneWriter,
}

impl StructuredCloneWriter {
    unsafe fn write_slice(&self, v: &[u8]) {
        let type_length = v.len();
        write_length(self.w, type_length);
        assert!(JS_WriteBytes(self.w, v.as_ptr() as *const raw::c_void, type_length));
    }
    unsafe fn write_str(&self, s: &str) {
        self.write_slice(s.as_bytes());
    }
}

struct StructuredCloneReader {
    r: *mut JSStructuredCloneReader,
}

impl StructuredCloneReader {
    unsafe fn read_bytes(&self) -> Vec<u8> {
        let mut bytes = vec![0u8; read_length(self.r)];
        let blob_length = bytes.len();
        assert!(JS_ReadBytes(self.r, bytes.as_mut_ptr() as *mut raw::c_void, blob_length));
        return bytes;
    }
    unsafe fn read_str(&self) -> String {
        let str_buffer = self.read_bytes();
        return String::from_utf8_unchecked(str_buffer);
    }
}

unsafe fn read_blob(cx: *mut JSContext,
                    r: *mut JSStructuredCloneReader)
                    -> *mut JSObject {
    let structured_reader = StructuredCloneReader { r: r };
    let blob_buffer = structured_reader.read_bytes();
    let type_str = structured_reader.read_str();
    let target_global = GlobalScope::from_context(cx);
    let blob = Blob::new(&target_global, BlobImpl::new_from_bytes(blob_buffer), type_str);
    return blob.reflector().get_jsobject().get()
}

unsafe fn write_blob(blob: Root<Blob>,
                     w: *mut JSStructuredCloneWriter)
                     -> Result<(), ()> {
    let structured_writer = StructuredCloneWriter { w: w };
    let blob_vec = try!(blob.get_bytes());
    assert!(JS_WriteUint32Pair(w, StructuredCloneTags::DomBlob as u32, 0));
    structured_writer.write_slice(&blob_vec);
    structured_writer.write_str(&blob.type_string());
    return Ok(())
}

unsafe extern "C" fn read_callback(cx: *mut JSContext,
                                   r: *mut JSStructuredCloneReader,
                                   tag: u32,
                                   _data: u32,
                                   _closure: *mut raw::c_void)
                                   -> *mut JSObject {
    assert!(tag < StructuredCloneTags::Max as u32, "tag should be lower than StructuredCloneTags::Max");
    assert!(tag > StructuredCloneTags::Min as u32, "tag should be higher than StructuredCloneTags::Min");
    if tag == StructuredCloneTags::DomBlob as u32 {
        return read_blob(cx, r)
    }
    return ptr::null_mut()
}

unsafe extern "C" fn write_callback(_cx: *mut JSContext,
                                    w: *mut JSStructuredCloneWriter,
                                    obj: HandleObject,
                                    _closure: *mut raw::c_void)
                                    -> bool {
    if let Ok(blob) = root_from_handleobject::<Blob>(obj) {
        return write_blob(blob, w).is_ok()
    }
    return false
}

unsafe extern "C" fn read_transfer_callback(_cx: *mut JSContext,
                                            _r: *mut JSStructuredCloneReader,
                                            _tag: u32,
                                            _content: *mut raw::c_void,
                                            _extra_data: u64,
                                            _closure: *mut raw::c_void,
                                            _return_object: MutableHandleObject)
                                            -> bool {
    false
}

unsafe extern "C" fn write_transfer_callback(_cx: *mut JSContext,
                                             _obj: Handle<*mut JSObject>,
                                             _closure: *mut raw::c_void,
                                             _tag: *mut u32,
                                             _ownership: *mut TransferableOwnership,
                                             _content:  *mut *mut raw::c_void,
                                             _extra_data: *mut u64)
                                             -> bool {
    false
}

unsafe extern "C" fn free_transfer_callback(_tag: u32,
                                            _ownership: TransferableOwnership,
                                            _content: *mut raw::c_void,
                                            _extra_data: u64,
                                            _closure: *mut raw::c_void) {
}

unsafe extern "C" fn report_error_callback(_cx: *mut JSContext, _errorid: u32) {
}

static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredCloneCallbacks {
    read: Some(read_callback),
    write: Some(write_callback),
    reportError: Some(report_error_callback),
    readTransfer: Some(read_transfer_callback),
    writeTransfer: Some(write_transfer_callback),
    freeTransfer: Some(free_transfer_callback),
};

/// A buffer for a structured clone.
pub enum StructuredCloneData {
    /// A non-serializable (default) variant
    Struct(*mut u64, size_t),
    /// A variant that can be serialized
    Vector(Vec<u8>)
}

impl StructuredCloneData {
    /// Writes a structured clone. Returns a `DataClone` error if that fails.
    pub fn write(cx: *mut JSContext, message: HandleValue) -> Fallible<StructuredCloneData> {
        let mut data = ptr::null_mut();
        let mut nbytes = 0;
        let result = unsafe {
            JS_WriteStructuredClone(cx,
                                    message,
                                    &mut data,
                                    &mut nbytes,
                                    &STRUCTURED_CLONE_CALLBACKS,
                                    ptr::null_mut(),
                                    HandleValue::undefined())
        };
        if !result {
            unsafe {
                JS_ClearPendingException(cx);
            }
            return Err(Error::DataClone);
        }
        Ok(StructuredCloneData::Struct(data, nbytes))
    }

    /// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing
    pub fn move_to_arraybuffer(self) -> Vec<u8> {
        match self {
            StructuredCloneData::Struct(data, nbytes) => {
                unsafe {
                    slice::from_raw_parts(data as *mut u8, nbytes).to_vec()
                }
            }
            StructuredCloneData::Vector(msg) => msg
        }
    }

    /// Reads a structured clone.
    ///
    /// Panics if `JS_ReadStructuredClone` fails.
    fn read_clone(global: &GlobalScope,
                  data: *mut u64,
                  nbytes: size_t,
                  rval: MutableHandleValue) {
        let cx = global.get_cx();
        let globalhandle = global.reflector().get_jsobject();
        let _ac = JSAutoCompartment::new(cx, globalhandle.get());
        unsafe {
            assert!(JS_ReadStructuredClone(cx,
                                           data,
                                           nbytes,
                                           JS_STRUCTURED_CLONE_VERSION,
                                           rval,
                                           &STRUCTURED_CLONE_CALLBACKS,
                                           ptr::null_mut()));
        }
    }

    /// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
    pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) {
        match self {
            StructuredCloneData::Vector(mut vec_msg) => {
                let nbytes = vec_msg.len();
                let data = vec_msg.as_mut_ptr() as *mut u64;
                StructuredCloneData::read_clone(global, data, nbytes, rval);
            }
            StructuredCloneData::Struct(data, nbytes) => StructuredCloneData::read_clone(global, data, nbytes, rval)
        }
    }
}

unsafe impl Send for StructuredCloneData {}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Read;
use std::mem::transmute;
use std::thread::sleep_ms;
use std::env;

fn main() {
    let delay = match env::args().nth(1).and_then(|s| s.parse::<f32>().ok()) {
        Some(d) => 60.0 * d,
        None => {
            println!("Usage: boot-delay <minutes>");
            return;
        }
    };

    let mut buf = [0u8; 1024];
    let uptime = File::open("/proc/uptime")
        .and_then(|ref mut file| file.read(&mut buf))
        .map(|sz| {
            buf.iter()
               .position(|&c| c == 0x20)
               .and_then(|p| if p < sz {
                   unsafe { transmute::<_, &str>(&buf[..p]) }.parse::<f32>().ok()
               } else {
                   None
               }).unwrap()
        })
        .unwrap();

    if delay > uptime {
        sleep_ms((delay - uptime) as u32 * 1000);
    }
}
<commit_msg>Fix deprecated sleep_ms(u32) warning, use sleep(Duration)<commit_after>use std::fs::File;
use std::io::Read;
use std::mem::transmute;
use std::thread::sleep;
use std::env;
use std::time::Duration;

fn main() {
    let delay = match env::args().nth(1).and_then(|s| s.parse::<f32>().ok()) {
        Some(d) => 60.0 * d,
        None => {
            println!("Usage: boot-delay <minutes>");
            return;
        }
    };

    let mut buf = [0u8; 1024];
    let uptime = File::open("/proc/uptime")
        .and_then(|ref mut file| file.read(&mut buf))
        .map(|sz| {
            buf.iter()
               .position(|&c| c == 0x20)
               .and_then(|p| if p < sz {
                   unsafe { transmute::<_, &str>(&buf[..p]) }.parse::<f32>().ok()
               } else {
                   None
               }).unwrap()
        })
        .unwrap();

    if delay > uptime {
        sleep(Duration::from_secs((delay - uptime) as u64));
    }
}
<|endoftext|>


<commit_before>extern crate cargo_metadata;

use std::path::{PathBuf, Path};
use std::io::Write;
use std::process::Command;

const CARGO_MIRI_HELP: &str = r#"Interprets bin crates

Usage:
    cargo miri [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

The feature `cargo-miri` is automatically defined for convenience. You can use
it to configure the resource limits

    #![cfg_attr(feature = "cargo-miri", memory_size = 42)]

available resource limits are `memory_size`, `step_limit`, `stack_limit`
"#;

fn show_help() {
    println!("{}", CARGO_MIRI_HELP);
}

fn show_version() {
    println!("miri {} ({} {})",
        env!("CARGO_PKG_VERSION"), env!("VERGEN_SHA_SHORT"), env!("VERGEN_COMMIT_DATE"));
}

fn main() {
    // Check for version and help flags even when invoked as 'cargo-miri'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is when `cargo miri` is called

        let test = std::env::args().nth(2).map_or(false, |text| text == "test");
        let skip = if test { 3 } else { 2 };

        let manifest_path_arg = std::env::args().skip(skip).find(|val| {
            val.starts_with("--manifest-path=")
        });

        let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(
            manifest_path_arg.as_ref().map(AsRef::as_ref),
        )
        {
            metadata
        } else {
            let _ = std::io::stderr().write_fmt(format_args!(
                "error: Could not obtain cargo metadata."
            ));
            std::process::exit(101);
        };

        let manifest_path = manifest_path_arg.map(|arg| {
            PathBuf::from(Path::new(&arg["--manifest-path=".len()..]))
        });

        let current_dir = std::env::current_dir();

        let package_index = metadata
            .packages
            .iter()
            .position(|package| {
                let package_manifest_path = Path::new(&package.manifest_path);
                if let Some(ref manifest_path) = manifest_path {
                    package_manifest_path == manifest_path
                } else {
                    let current_dir = current_dir.as_ref().expect(
                        "could not read current directory",
                    );
                    let package_manifest_directory = package_manifest_path.parent().expect(
                        "could not find parent directory of package manifest",
                    );
                    package_manifest_directory == current_dir
                }
            })
            .expect("could not find matching package");
        let package = metadata.packages.remove(package_index);
        for target in package.targets {
            let args = std::env::args().skip(skip);
            let kind = target.kind.get(0).expect(
                "badly formatted cargo metadata: target::kind is an empty array",
            );
            if test && kind == "test" {
                if let Err(code) = process(
                    vec!["--test".to_string(), target.name].into_iter().chain(
                        args,
                    ),
                )
                {
                    std::process::exit(code);
                }
            } else if !test && kind == "bin" {
                if let Err(code) = process(
                    vec!["--bin".to_string(), target.name].into_iter().chain(
                        args,
                    ),
                )
                {
                    std::process::exit(code);
                }
            }
        }
    } else {
        // this arm is executed when cargo-miri runs `cargo rustc` with the `RUSTC` env var set to itself

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let Ok(sysroot) = ::std::env::var("MIRI_SYSROOT") {
            sysroot
        } else if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("RUST_SYSROOT")
                .map(|s| s.to_owned())
                .or_else(|| {
                    Command::new("rustc")
                        .arg("--print")
                        .arg("sysroot")
                        .output()
                        .ok()
                        .and_then(|out| String::from_utf8(out.stdout).ok())
                        .map(|s| s.trim().to_owned())
                })
                .expect("need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = if std::env::args().any(|s| s == "--sysroot") {
            std::env::args().skip(1).collect()
        } else {
            std::env::args()
                .skip(1)
                .chain(Some("--sysroot".to_owned()))
                .chain(Some(sys_root))
                .collect()
        };

        // this check ensures that dependencies are built but not interpreted and the final crate is
        // interpreted but not built
        let miri_enabled = std::env::args().any(|s| s == "--emit=dep-info,metadata");

        let mut command = if miri_enabled {
            let mut path = std::env::current_exe().expect("current executable path invalid");
            path.set_file_name("miri");
            Command::new(path)
        } else {
            Command::new("rustc")
        };

        args.extend_from_slice(&["-Z".to_owned(), "always-encode-mir".to_owned()]);
        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]);

        match command.args(&args).status() {
            Ok(exit) => {
                if !exit.success() {
                    std::process::exit(exit.code().unwrap_or(42));
                }
            }
            Err(ref e) if miri_enabled => panic!("error during miri run: {:?}", e),
            Err(ref e) => panic!("error during rustc call: {:?}", e),
        }
    }
}

fn process<I>(old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("--emit=dep-info,metadata".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-miri""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<commit_msg>cargo-miri: support running unit tests for libraries as well as test binaries<commit_after>extern crate cargo_metadata;

use std::path::{PathBuf, Path};
use std::io::Write;
use std::process::Command;

const CARGO_MIRI_HELP: &str = r#"Interprets bin crates

Usage:
    cargo miri [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

The feature `cargo-miri` is automatically defined for convenience. You can use
it to configure the resource limits

    #![cfg_attr(feature = "cargo-miri", memory_size = 42)]

available resource limits are `memory_size`, `step_limit`, `stack_limit`
"#;

fn show_help() {
    println!("{}", CARGO_MIRI_HELP);
}

fn show_version() {
    println!("miri {} ({} {})",
        env!("CARGO_PKG_VERSION"), env!("VERGEN_SHA_SHORT"), env!("VERGEN_COMMIT_DATE"));
}

fn main() {
    // Check for version and help flags even when invoked as 'cargo-miri'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is when `cargo miri` is called

        let test = std::env::args().nth(2).map_or(false, |text| text == "test");
        let skip = if test { 3 } else { 2 };

        let manifest_path_arg = std::env::args().skip(skip).find(|val| {
            val.starts_with("--manifest-path=")
        });

        let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(
            manifest_path_arg.as_ref().map(AsRef::as_ref),
        )
        {
            metadata
        } else {
            let _ = std::io::stderr().write_fmt(format_args!(
                "error: Could not obtain cargo metadata."
            ));
            std::process::exit(101);
        };

        let manifest_path = manifest_path_arg.map(|arg| {
            PathBuf::from(Path::new(&arg["--manifest-path=".len()..]))
        });

        let current_dir = std::env::current_dir();

        let package_index = metadata
            .packages
            .iter()
            .position(|package| {
                let package_manifest_path = Path::new(&package.manifest_path);
                if let Some(ref manifest_path) = manifest_path {
                    package_manifest_path == manifest_path
                } else {
                    let current_dir = current_dir.as_ref().expect(
                        "could not read current directory",
                    );
                    let package_manifest_directory = package_manifest_path.parent().expect(
                        "could not find parent directory of package manifest",
                    );
                    package_manifest_directory == current_dir
                }
            })
            .expect("could not find matching package");
        let package = metadata.packages.remove(package_index);
        for target in package.targets {
            let args = std::env::args().skip(skip);
            let kind = target.kind.get(0).expect(
                "badly formatted cargo metadata: target::kind is an empty array",
            );
            match (test, &kind[..]) {
                (true, "test") => {
                    if let Err(code) = process(
                        vec!["--test".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (true, "lib") => {
                    if let Err(code) = process(
                        vec!["--".to_string(), "--test".to_string()].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                (false, "bin") => {
                    if let Err(code) = process(
                        vec!["--bin".to_string(), target.name].into_iter().chain(
                            args,
                        ),
                    )
                    {
                        std::process::exit(code);
                    }
                }
                _ => {}
            }
        }
    } else {
        // this arm is executed when cargo-miri runs `cargo rustc` with the `RUSTC` env var set to itself

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let Ok(sysroot) = ::std::env::var("MIRI_SYSROOT") {
            sysroot
        } else if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("RUST_SYSROOT")
                .map(|s| s.to_owned())
                .or_else(|| {
                    Command::new("rustc")
                        .arg("--print")
                        .arg("sysroot")
                        .output()
                        .ok()
                        .and_then(|out| String::from_utf8(out.stdout).ok())
                        .map(|s| s.trim().to_owned())
                })
                .expect("need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = if std::env::args().any(|s| s == "--sysroot") {
            std::env::args().skip(1).collect()
        } else {
            std::env::args()
                .skip(1)
                .chain(Some("--sysroot".to_owned()))
                .chain(Some(sys_root))
                .collect()
        };

        // this check ensures that dependencies are built but not interpreted and the final crate is
        // interpreted but not built
        let miri_enabled = std::env::args().any(|s| s == "--emit=dep-info,metadata");

        let mut command = if miri_enabled {
            let mut path = std::env::current_exe().expect("current executable path invalid");
            path.set_file_name("miri");
            Command::new(path)
        } else {
            Command::new("rustc")
        };

        args.extend_from_slice(&["-Z".to_owned(), "always-encode-mir".to_owned()]);
        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]);

        match command.args(&args).status() {
            Ok(exit) => {
                if !exit.success() {
                    std::process::exit(exit.code().unwrap_or(42));
                }
            }
            Err(ref e) if miri_enabled => panic!("error during miri run: {:?}", e),
            Err(ref e) => panic!("error during rustc call: {:?}", e),
        }
    }
}

fn process<I>(old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("--emit=dep-info,metadata".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-miri""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<|endoftext|>