<commit_before>//! TODO Fill in

use rustwlc::{Geometry, Point, Size, WlcOutput};
use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value};
use super::object::{Object, Objectable};
use super::property::Property;
use super::class::{self, Class, ClassBuilder};

pub const SCREENS_HANDLE: &'static str = "__screens";

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Output {
    pub name: String,
    pub mm_width: u32,
    pub mm_height: u32,
    // TODO The XID array?
}

impl From<WlcOutput> for Output {
    fn from(output: WlcOutput) -> Output {
        let resolution = output.get_resolution().unwrap();
        Output {
            name: output.get_name(),
            mm_width: resolution.w,
            mm_height: resolution.h
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ScreenState {
    // Is this screen still valid and may be used
    pub valid: bool,
    // Screen geometry
    pub geometry: Geometry,
    // Screen workarea
    pub workarea: Geometry,
    // The screen outputs information
    pub outputs: Vec<Output>,
    // Some XID indetifying this screen
    pub xid: u32
}

pub struct Screen<'lua>(Table<'lua>);

impl_objectable!(Screen, ScreenState);

impl Display for ScreenState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Screen: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Screen<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for ScreenState {}

impl Default for ScreenState {
    fn default() -> Self {
        ScreenState {
            valid: true,
            geometry: Geometry::zero(),
            workarea: Geometry::zero(),
            outputs: vec![],
            xid: 0
        }
    }
}

impl <'lua> Screen<'lua> {
    fn new(lua: &Lua) -> rlua::Result<Object> {
        let class = class::class_setup(lua, "screen")?;
        Ok(Screen::allocate(lua, class)?.build())
    }

    fn init_screens(&mut self, outputs: Vec<Output>) -> rlua::Result<()> {
        let mut state = self.state()?;
        state.outputs = outputs;
        self.set_state(state)
    }

    fn get_geometry(&self, lua: &'lua Lua) -> rlua::Result<Table<'lua>> {
        let state = self.state()?;
        let Point { x, y } = state.geometry.origin;
        let Size { w, h } = state.geometry.size;
        // TODO I do this a lot, put it somewhere
        let table = lua.create_table();
        table.set("x", x)?;
        table.set("y", y)?;
        table.set("width", w)?;
        table.set("height", h)?;
        Ok(table)
    }

    fn get_workarea(&self, lua: &'lua Lua) -> rlua::Result<Table<'lua>> {
        let state = self.state()?;
        let Point { x, y } = state.workarea.origin;
        let Size { w, h } = state.workarea.size;
        // TODO I do this a lot, put it somewhere
        let table = lua.create_table();
        table.set("x", x)?;
        table.set("y", y)?;
        table.set("width", w)?;
        table.set("height", h)?;
        Ok(table)
    }
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    let res = property_setup(lua, method_setup(lua, Class::builder(lua, "screen", Some(Rc::new(Screen::new)), None, None)?)?)?
        .save_class("screen")?
        .build()?;
    let mut screens: Vec<Screen> = vec![];
    for output in WlcOutput::list() {
        let mut screen = Screen::cast(Screen::new(lua)?)?;
        screen.init_screens(vec![output.into()])?;
        // TODO Move to Screen impl like the others
        screens.push(screen);
    }
    // TODO Uncomment
    // This breaks rc.lua because of layoutbox stuff.
    // Please fix that when you uncomment this.
    lua.globals().set(SCREENS_HANDLE, lua.create_table()/*screens.to_lua(lua)?*/)?;
    Ok(res)
}

fn method_setup<'lua>(lua: &'lua Lua, builder: ClassBuilder<'lua>) -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    use super::dummy;
    builder.method("connect_signal".into(), lua.create_function(dummy))?
           .method("__index".into(), lua.create_function(index))?
           .method("__call".into(), lua.create_function(iterate_over_screens))
}

fn property_setup<'lua>(lua: &'lua Lua, builder: ClassBuilder<'lua>) -> rlua::Result<ClassBuilder<'lua>> {
    builder
        .property(Property::new("geometry".into(),
                                None,
                                Some(lua.create_function(get_geometry)),
                                None))?
        .property(Property::new("workarea".into(),
                                None,
                                Some(lua.create_function(get_workarea)),
                                None))
}

fn get_geometry<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    let screen = Screen::cast(table.into())?;
    screen.get_geometry(lua)
}

fn get_workarea<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    let screen = Screen::cast(table.into())?;
    screen.get_workarea(lua)
}

/// Ok this requires some explanation...
/// Lua gives us the previous value in the loop, with the first one being nil
/// since there was nothing there before.
///
/// To ensure we loop over everything, we take the index of that value in our list,
/// increment it by 1 (starting at 0 if it's the start) and then once it falls outside
/// the bounds it will stop by returning nil.
fn iterate_over_screens<'lua>(lua: &'lua Lua,
                              (_, _, prev): (Value<'lua>, Value<'lua>, Value<'lua>))
                              -> rlua::Result<Value<'lua>> {
    let screens: Vec<Screen> = lua.globals().get::<_, Vec<Table>>(SCREENS_HANDLE)?
        .into_iter().map(|t| Screen::cast(t.into()).unwrap())
        .collect();
    let index = match prev {
        Value::Nil => 0,
        Value::Table(ref table) => {
            if let Ok(screen) = Screen::cast(table.clone().into()) {
                screens.iter().position(|t| t.state().unwrap() == screen.state().unwrap())
                    .unwrap_or(screens.len()) + 1
            } else {
                panic!("Unexpected non-screen table in loop");
            }
        }
        _ => panic!("Unexpected non-screen or nil value in screens loop")
    };
    if index < screens.len() {
        screens[index].get_table().to_lua(lua)
    } else {
        Ok(Value::Nil)
    }

}

fn index<'lua>(lua: &'lua Lua,
               (obj_table, index): (Table<'lua>, Value<'lua>))
               -> rlua::Result<Value<'lua>> {
    let screens: Vec<Screen> = lua.globals().get::<_, Vec<Table>>(SCREENS_HANDLE)?
        .into_iter().map(|t| Screen::cast(t.into()).unwrap())
        .collect();
    match index {
        Value::String(ref string) => {
            let string = string.to_str()?;
            if string == "primary" {
                // TODO Emit primary changed signal
                if screens.len() > 0 {
                    return screens[0].get_table().clone().to_lua(lua)
                }
            }
            for screen in screens.iter() {
                let screen_state = screen.state()?;
                for output in &screen_state.outputs {
                    if output.name.as_str() == string {
                        return screen.get_table().clone().to_lua(lua)
                    }
                }
            }
        },
        // TODO Might need to do Number instead
        Value::Integer(screen_index) => {
            if screen_index < 1 || screen_index as usize > screens.len() {
                return Err(rlua::Error::RuntimeError(
                    format!("invalid screen number: {} (of {} existing)",
                            screen_index, screens.len())))
            }
            return screens[screen_index as usize].get_table().clone().to_lua(lua).clone()
        },
        Value::Table(ref table) => {
            // If this is a screen, just return it
            if let Ok(screen) = Screen::cast(table.clone().into()) {
                return screen.to_lua(lua)
            }
        },
        // TODO This checke user data like in luaA_toudata in awesome
        _ => {}
    }
    // TODO checkudata
    let meta = obj_table.get_metatable().unwrap();
    meta.get(index.clone()).or_else(|_| super::object::default_index(lua, (obj_table, index)))
}
<commit_msg>screen: Translate WLC outputs to screen objects<commit_after>//! TODO Fill in

use rustwlc::{Geometry, Point, Size, WlcOutput};
use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value};
use super::object::{Object, Objectable};
use super::property::Property;
use super::class::{self, Class, ClassBuilder};

pub const SCREENS_HANDLE: &'static str = "__screens";

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Output {
    pub name: String,
    pub mm_width: u32,
    pub mm_height: u32,
    // TODO The XID array?
}

impl From<WlcOutput> for Output {
    fn from(output: WlcOutput) -> Output {
        let resolution = output.get_resolution().unwrap();
        Output {
            name: output.get_name(),
            mm_width: resolution.w,
            mm_height: resolution.h
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ScreenState {
    // Is this screen still valid and may be used
    pub valid: bool,
    // Screen geometry
    pub geometry: Geometry,
    // Screen workarea
    pub workarea: Geometry,
    // The screen outputs information
    pub outputs: Vec<Output>,
    // Some XID indetifying this screen
    pub xid: u32
}

pub struct Screen<'lua>(Table<'lua>);

impl_objectable!(Screen, ScreenState);

impl Display for ScreenState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Screen: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Screen<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for ScreenState {}

impl Default for ScreenState {
    fn default() -> Self {
        ScreenState {
            valid: true,
            geometry: Geometry::zero(),
            workarea: Geometry::zero(),
            outputs: vec![],
            xid: 0
        }
    }
}

impl <'lua> Screen<'lua> {
    fn new(lua: &Lua) -> rlua::Result<Object> {
        let class = class::class_setup(lua, "screen")?;
        Ok(Screen::allocate(lua, class)?.build())
    }

    fn init_screens(&mut self, outputs: Vec<Output>) -> rlua::Result<()> {
        let mut state = self.state()?;
        state.outputs = outputs;
        self.set_state(state)
    }

    fn get_geometry(&self, lua: &'lua Lua) -> rlua::Result<Table<'lua>> {
        let state = self.state()?;
        let Point { x, y } = state.geometry.origin;
        let Size { w, h } = state.geometry.size;
        // TODO I do this a lot, put it somewhere
        let table = lua.create_table();
        table.set("x", x)?;
        table.set("y", y)?;
        table.set("width", w)?;
        table.set("height", h)?;
        Ok(table)
    }

    fn get_workarea(&self, lua: &'lua Lua) -> rlua::Result<Table<'lua>> {
        let state = self.state()?;
        let Point { x, y } = state.workarea.origin;
        let Size { w, h } = state.workarea.size;
        // TODO I do this a lot, put it somewhere
        let table = lua.create_table();
        table.set("x", x)?;
        table.set("y", y)?;
        table.set("width", w)?;
        table.set("height", h)?;
        Ok(table)
    }
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    let res = property_setup(lua, method_setup(lua, Class::builder(lua, "screen", Some(Rc::new(Screen::new)), None, None)?)?)?
        .save_class("screen")?
        .build()?;
    let mut screens: Vec<Screen> = vec![];
    for output in WlcOutput::list() {
        let mut screen = Screen::cast(Screen::new(lua)?)?;
        screen.init_screens(vec![output.into()])?;
        // TODO Move to Screen impl like the others
        screens.push(screen);
    }
    lua.globals().set(SCREENS_HANDLE, screens.to_lua(lua)?)?;
    Ok(res)
}

fn method_setup<'lua>(lua: &'lua Lua, builder: ClassBuilder<'lua>) -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    use super::dummy;
    builder.method("connect_signal".into(), lua.create_function(dummy))?
           .method("__index".into(), lua.create_function(index))?
           .method("__call".into(), lua.create_function(iterate_over_screens))
}

fn property_setup<'lua>(lua: &'lua Lua, builder: ClassBuilder<'lua>) -> rlua::Result<ClassBuilder<'lua>> {
    builder
        .property(Property::new("geometry".into(),
                                None,
                                Some(lua.create_function(get_geometry)),
                                None))?
        .property(Property::new("workarea".into(),
                                None,
                                Some(lua.create_function(get_workarea)),
                                None))
}

fn get_geometry<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    let screen = Screen::cast(table.into())?;
    screen.get_geometry(lua)
}

fn get_workarea<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    let screen = Screen::cast(table.into())?;
    screen.get_workarea(lua)
}

/// Ok this requires some explanation...
/// Lua gives us the previous value in the loop, with the first one being nil
/// since there was nothing there before.
///
/// To ensure we loop over everything, we take the index of that value in our list,
/// increment it by 1 (starting at 0 if it's the start) and then once it falls outside
/// the bounds it will stop by returning nil.
fn iterate_over_screens<'lua>(lua: &'lua Lua,
                              (_, _, prev): (Value<'lua>, Value<'lua>, Value<'lua>))
                              -> rlua::Result<Value<'lua>> {
    let screens: Vec<Screen> = lua.globals().get::<_, Vec<Table>>(SCREENS_HANDLE)?
        .into_iter().map(|t| Screen::cast(t.into()).unwrap())
        .collect();
    let index = match prev {
        Value::Nil => 0,
        Value::Table(ref table) => {
            if let Ok(screen) = Screen::cast(table.clone().into()) {
                screens.iter().position(|t| t.state().unwrap() == screen.state().unwrap())
                    .unwrap_or(screens.len()) + 1
            } else {
                panic!("Unexpected non-screen table in loop");
            }
        }
        _ => panic!("Unexpected non-screen or nil value in screens loop")
    };
    if index < screens.len() {
        screens[index].get_table().to_lua(lua)
    } else {
        Ok(Value::Nil)
    }

}

fn index<'lua>(lua: &'lua Lua,
               (obj_table, index): (Table<'lua>, Value<'lua>))
               -> rlua::Result<Value<'lua>> {
    let screens: Vec<Screen> = lua.globals().get::<_, Vec<Table>>(SCREENS_HANDLE)?
        .into_iter().map(|t| Screen::cast(t.into()).unwrap())
        .collect();
    match index {
        Value::String(ref string) => {
            let string = string.to_str()?;
            if string == "primary" {
                // TODO Emit primary changed signal
                if screens.len() > 0 {
                    return screens[0].get_table().clone().to_lua(lua)
                }
            }
            for screen in screens.iter() {
                let screen_state = screen.state()?;
                for output in &screen_state.outputs {
                    if output.name.as_str() == string {
                        return screen.get_table().clone().to_lua(lua)
                    }
                }
            }
        },
        // TODO Might need to do Number instead
        Value::Integer(screen_index) => {
            if screen_index < 1 || screen_index as usize > screens.len() {
                return Err(rlua::Error::RuntimeError(
                    format!("invalid screen number: {} (of {} existing)",
                            screen_index, screens.len())))
            }
            return screens[screen_index as usize].get_table().clone().to_lua(lua).clone()
        },
        Value::Table(ref table) => {
            // If this is a screen, just return it
            if let Ok(screen) = Screen::cast(table.clone().into()) {
                return screen.to_lua(lua)
            }
        },
        // TODO This checke user data like in luaA_toudata in awesome
        _ => {}
    }
    // TODO checkudata
    let meta = obj_table.get_metatable().unwrap();
    meta.get(index.clone()).or_else(|_| super::object::default_index(lua, (obj_table, index)))
}
<|endoftext|>


<commit_before>#![crate_name="cargo-test"]
#![feature(phase)]

#[phase(plugin, link)]
extern crate cargo;
extern crate serialize;

#[phase(plugin, link)]
extern crate hammer;

use std::os;
use std::io::{UserExecute, fs};

use cargo::ops;
use cargo::{execute_main_without_stdin};
use cargo::core::{MultiShell};
use cargo::util;
use cargo::util::{CliResult, CliError, CargoError};
use cargo::util::important_paths::find_project_manifest;

#[deriving(PartialEq,Clone,Decodable)]
struct Options {
    manifest_path: Option<String>,
    jobs: Option<uint>,
    rest: Vec<String>,
}

hammer_config!(Options "Run the package's test suite", |c| {
    c.short("jobs", 'j')
})

fn main() {
    execute_main_without_stdin(execute);
}

fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
    let root = match options.manifest_path {
        Some(path) => Path::new(path),
        None => try!(find_project_manifest(&os::getcwd(), "Cargo.toml")
                    .map_err(|_| {
                        CliError::new("Could not find Cargo.toml in this \
                                       directory or any parent directory",
                                      102)
                    }))
    };

    let compile_opts = ops::CompileOptions {
        update: false,
        env: "test",
        shell: shell,
        jobs: options.jobs
    };

    try!(ops::compile(&root, compile_opts).map(|_| None::<()>).map_err(|err| {
        CliError::from_boxed(err, 101)
    }));

    let test_dir = root.dir_path().join("target").join("test");

    let mut walk = try!(fs::walk_dir(&test_dir).map_err(|e| {
        CliError::from_error(e, 1)
    }));

    for file in walk {
        // TODO: The proper fix is to have target knows its expected
        // output and only run expected executables.
        if file.display().to_string().as_slice().contains("dSYM") { continue; }
        if !is_executable(&file) { continue; }

        try!(util::process(file).exec().map_err(|e| {
            CliError::from_boxed(e.box_error(), 1)
        }));
    }

    Ok(None)
}

fn is_executable(path: &Path) -> bool {
    if !path.is_file() { return false; }
    path.stat().map(|stat| stat.perm.intersects(UserExecute)).unwrap_or(false)
}
<commit_msg>Add -u to `cargo test`<commit_after>#![crate_name="cargo-test"]
#![feature(phase)]

#[phase(plugin, link)]
extern crate cargo;
extern crate serialize;

#[phase(plugin, link)]
extern crate hammer;

use std::os;
use std::io::{UserExecute, fs};

use cargo::ops;
use cargo::{execute_main_without_stdin};
use cargo::core::{MultiShell};
use cargo::util;
use cargo::util::{CliResult, CliError, CargoError};
use cargo::util::important_paths::find_project_manifest;

#[deriving(PartialEq,Clone,Decodable)]
struct Options {
    manifest_path: Option<String>,
    jobs: Option<uint>,
    update: bool,
    rest: Vec<String>,
}

hammer_config!(Options "Run the package's test suite", |c| {
    c.short("jobs", 'j').short("update", 'u')
})

fn main() {
    execute_main_without_stdin(execute);
}

fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
    let root = match options.manifest_path {
        Some(path) => Path::new(path),
        None => try!(find_project_manifest(&os::getcwd(), "Cargo.toml")
                    .map_err(|_| {
                        CliError::new("Could not find Cargo.toml in this \
                                       directory or any parent directory",
                                      102)
                    }))
    };

    let compile_opts = ops::CompileOptions {
        update: options.update,
        env: "test",
        shell: shell,
        jobs: options.jobs
    };

    try!(ops::compile(&root, compile_opts).map(|_| None::<()>).map_err(|err| {
        CliError::from_boxed(err, 101)
    }));

    let test_dir = root.dir_path().join("target").join("test");

    let mut walk = try!(fs::walk_dir(&test_dir).map_err(|e| {
        CliError::from_error(e, 1)
    }));

    for file in walk {
        // TODO: The proper fix is to have target knows its expected
        // output and only run expected executables.
        if file.display().to_string().as_slice().contains("dSYM") { continue; }
        if !is_executable(&file) { continue; }

        try!(util::process(file).exec().map_err(|e| {
            CliError::from_boxed(e.box_error(), 1)
        }));
    }

    Ok(None)
}

fn is_executable(path: &Path) -> bool {
    if !path.is_file() { return false; }
    path.stat().map(|stat| stat.perm.intersects(UserExecute)).unwrap_or(false)
}
<|endoftext|>


<commit_before>use super::{Open, Sink, SinkAsBytes};
use crate::config::AudioFormat;
use crate::convert::Converter;
use crate::decoder::AudioPacket;
use crate::{NUM_CHANNELS, SAMPLE_RATE};
use alsa::device_name::HintIter;
use alsa::pcm::{Access, Format, HwParams, PCM};
use alsa::{Direction, ValueOr};
use std::cmp::min;
use std::io;
use std::process::exit;
use std::time::Duration;
use thiserror::Error;

// 0.5 sec buffer.
const PERIOD_TIME: Duration = Duration::from_millis(100);
const BUFFER_TIME: Duration = Duration::from_millis(500);

#[derive(Debug, Error)]
enum AlsaError {
    #[error("AlsaSink, device {device} may be invalid or busy, {err}")]
    PcmSetUp { device: String, err: alsa::Error },
    #[error("AlsaSink, device {device} unsupported access type RWInterleaved, {err}")]
    UnsupportedAccessType { device: String, err: alsa::Error },
    #[error("AlsaSink, device {device} unsupported format {format:?}, {err}")]
    UnsupportedFormat {
        device: String,
        format: AudioFormat,
        err: alsa::Error,
    },
    #[error("AlsaSink, device {device} unsupported sample rate {samplerate}, {err}")]
    UnsupportedSampleRate {
        device: String,
        samplerate: u32,
        err: alsa::Error,
    },
    #[error("AlsaSink, device {device} unsupported channel count {channel_count}, {err}")]
    UnsupportedChannelCount {
        device: String,
        channel_count: u8,
        err: alsa::Error,
    },
    #[error("AlsaSink Hardware Parameters Error, {0}")]
    HwParams(alsa::Error),
    #[error("AlsaSink Software Parameters Error, {0}")]
    SwParams(alsa::Error),
    #[error("AlsaSink PCM Error, {0}")]
    Pcm(alsa::Error),
}

pub struct AlsaSink {
    pcm: Option<PCM>,
    format: AudioFormat,
    device: String,
    period_buffer: Vec<u8>,
}

fn list_outputs() -> io::Result<()> {
    println!("Listing available Alsa outputs:");
    for t in &["pcm", "ctl", "hwdep"] {
        println!("{} devices:", t);
        let i = match HintIter::new_str(None, &t) {
            Ok(i) => i,
            Err(e) => {
                return Err(io::Error::new(io::ErrorKind::Other, e));
            }
        };
        for a in i {
            if let Some(Direction::Playback) = a.direction {
                // mimic aplay -L
                let name = a
                    .name
                    .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not parse name"))?;
                let desc = a
                    .desc
                    .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not parse desc"))?;
                println!("{}\n\t{}\n", name, desc.replace("\n", "\n\t"));
            }
        }
    }

    Ok(())
}

fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, usize), AlsaError> {
    let pcm = PCM::new(dev_name, Direction::Playback, false).map_err(|e| AlsaError::PcmSetUp {
        device: dev_name.to_string(),
        err: e,
    })?;

    let alsa_format = match format {
        AudioFormat::F64 => Format::float64(),
        AudioFormat::F32 => Format::float(),
        AudioFormat::S32 => Format::s32(),
        AudioFormat::S24 => Format::s24(),
        AudioFormat::S16 => Format::s16(),

        #[cfg(target_endian = "little")]
        AudioFormat::S24_3 => Format::S243LE,
        #[cfg(target_endian = "big")]
        AudioFormat::S24_3 => Format::S243BE,
    };

    let bytes_per_period = {
        let hwp = HwParams::any(&pcm).map_err(AlsaError::HwParams)?;
        hwp.set_access(Access::RWInterleaved)
            .map_err(|e| AlsaError::UnsupportedAccessType {
                device: dev_name.to_string(),
                err: e,
            })?;

        hwp.set_format(alsa_format)
            .map_err(|e| AlsaError::UnsupportedFormat {
                device: dev_name.to_string(),
                format,
                err: e,
            })?;

        hwp.set_rate(SAMPLE_RATE, ValueOr::Nearest).map_err(|e| {
            AlsaError::UnsupportedSampleRate {
                device: dev_name.to_string(),
                samplerate: SAMPLE_RATE,
                err: e,
            }
        })?;

        hwp.set_channels(NUM_CHANNELS as u32)
            .map_err(|e| AlsaError::UnsupportedChannelCount {
                device: dev_name.to_string(),
                channel_count: NUM_CHANNELS,
                err: e,
            })?;

        hwp.set_buffer_time_near(BUFFER_TIME.as_micros() as u32, ValueOr::Nearest)
            .map_err(AlsaError::HwParams)?;

        hwp.set_period_time_near(PERIOD_TIME.as_micros() as u32, ValueOr::Nearest)
            .map_err(AlsaError::HwParams)?;

        pcm.hw_params(&hwp).map_err(AlsaError::Pcm)?;

        let swp = pcm.sw_params_current().map_err(AlsaError::Pcm)?;

        // Don't assume we got what we wanted.
        // Ask to make sure.
        let frames_per_period = hwp.get_period_size().map_err(AlsaError::HwParams)?;

        let frames_per_buffer = hwp.get_buffer_size().map_err(AlsaError::HwParams)?;

        swp.set_start_threshold(frames_per_buffer - frames_per_period)
            .map_err(AlsaError::SwParams)?;

        pcm.sw_params(&swp).map_err(AlsaError::Pcm)?;

        // Let ALSA do the math for us.
        pcm.frames_to_bytes(frames_per_period) as usize
    };

    Ok((pcm, bytes_per_period))
}

impl Open for AlsaSink {
    fn open(device: Option<String>, format: AudioFormat) -> Self {
        let name = match device.as_deref() {
            Some("?") => match list_outputs() {
                Ok(_) => {
                    exit(0);
                }
                Err(err) => {
                    error!("Error listing Alsa outputs, {}", err);
                    exit(1);
                }
            },
            Some(device) => device,
            None => "default",
        }
        .to_string();

        info!("Using AlsaSink with format: {:?}", format);

        Self {
            pcm: None,
            format,
            device: name,
            period_buffer: vec![],
        }
    }
}

impl Sink for AlsaSink {
    fn start(&mut self) -> io::Result<()> {
        if self.pcm.is_none() {
            match open_device(&self.device, self.format) {
                Ok((pcm, bytes_per_period)) => {
                    self.pcm = Some(pcm);
                    self.period_buffer = Vec::with_capacity(bytes_per_period);
                }
                Err(e) => {
                    return Err(io::Error::new(io::ErrorKind::Other, e));
                }
            }
        }

        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        {
            // Write any leftover data in the period buffer
            // before draining the actual buffer
            self.write_bytes(&[])?;
            let pcm = self.pcm.as_mut().ok_or_else(|| {
                io::Error::new(io::ErrorKind::Other, "Error stopping AlsaSink, PCM is None")
            })?;
            pcm.drain().map_err(|e| {
                io::Error::new(
                    io::ErrorKind::Other,
                    format!("Error stopping AlsaSink {}", e),
                )
            })?
        }
        self.pcm = None;
        Ok(())
    }

    sink_as_bytes!();
}

impl SinkAsBytes for AlsaSink {
    fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> {
        let mut processed_data = 0;
        while processed_data < data.len() {
            let data_to_buffer = min(
                self.period_buffer.capacity() - self.period_buffer.len(),
                data.len() - processed_data,
            );
            self.period_buffer
                .extend_from_slice(&data[processed_data..processed_data + data_to_buffer]);
            processed_data += data_to_buffer;
            if self.period_buffer.len() == self.period_buffer.capacity() {
                self.write_buf()?;
                self.period_buffer.clear();
            }
        }

        Ok(())
    }
}

impl AlsaSink {
    pub const NAME: &'static str = "alsa";

    fn write_buf(&mut self) -> io::Result<()> {
        let pcm = self.pcm.as_mut().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::Other,
                "Error writing from AlsaSink buffer to PCM, PCM is None",
            )
        })?;
        let io = pcm.io_bytes();
        if let Err(err) = io.writei(&self.period_buffer) {
            // Capture and log the original error as a warning, and then try to recover.
            // If recovery fails then forward that error back to player.
            warn!(
                "Error writing from AlsaSink buffer to PCM, trying to recover {}",
                err
            );
            pcm.try_recover(err, false).map_err(|e| {
                io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "Error writing from AlsaSink buffer to PCM, recovery failed {}",
                        e
                    ),
                )
            })?
        }

        Ok(())
    }
}
<commit_msg>Improve Alsa backend buffer (#811)<commit_after>use super::{Open, Sink, SinkAsBytes};
use crate::config::AudioFormat;
use crate::convert::Converter;
use crate::decoder::AudioPacket;
use crate::{NUM_CHANNELS, SAMPLE_RATE};
use alsa::device_name::HintIter;
use alsa::pcm::{Access, Format, HwParams, PCM};
use alsa::{Direction, ValueOr};
use std::cmp::min;
use std::io;
use std::process::exit;
use std::time::Duration;
use thiserror::Error;

// 0.5 sec buffer.
const PERIOD_TIME: Duration = Duration::from_millis(100);
const BUFFER_TIME: Duration = Duration::from_millis(500);

#[derive(Debug, Error)]
enum AlsaError {
    #[error("AlsaSink, device {device} may be invalid or busy, {err}")]
    PcmSetUp { device: String, err: alsa::Error },
    #[error("AlsaSink, device {device} unsupported access type RWInterleaved, {err}")]
    UnsupportedAccessType { device: String, err: alsa::Error },
    #[error("AlsaSink, device {device} unsupported format {format:?}, {err}")]
    UnsupportedFormat {
        device: String,
        format: AudioFormat,
        err: alsa::Error,
    },
    #[error("AlsaSink, device {device} unsupported sample rate {samplerate}, {err}")]
    UnsupportedSampleRate {
        device: String,
        samplerate: u32,
        err: alsa::Error,
    },
    #[error("AlsaSink, device {device} unsupported channel count {channel_count}, {err}")]
    UnsupportedChannelCount {
        device: String,
        channel_count: u8,
        err: alsa::Error,
    },
    #[error("AlsaSink Hardware Parameters Error, {0}")]
    HwParams(alsa::Error),
    #[error("AlsaSink Software Parameters Error, {0}")]
    SwParams(alsa::Error),
    #[error("AlsaSink PCM Error, {0}")]
    Pcm(alsa::Error),
}

pub struct AlsaSink {
    pcm: Option<PCM>,
    format: AudioFormat,
    device: String,
    period_buffer: Vec<u8>,
}

fn list_outputs() -> io::Result<()> {
    println!("Listing available Alsa outputs:");
    for t in &["pcm", "ctl", "hwdep"] {
        println!("{} devices:", t);
        let i = match HintIter::new_str(None, &t) {
            Ok(i) => i,
            Err(e) => {
                return Err(io::Error::new(io::ErrorKind::Other, e));
            }
        };
        for a in i {
            if let Some(Direction::Playback) = a.direction {
                // mimic aplay -L
                let name = a
                    .name
                    .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not parse name"))?;
                let desc = a
                    .desc
                    .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not parse desc"))?;
                println!("{}\n\t{}\n", name, desc.replace("\n", "\n\t"));
            }
        }
    }

    Ok(())
}

fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, usize), AlsaError> {
    let pcm = PCM::new(dev_name, Direction::Playback, false).map_err(|e| AlsaError::PcmSetUp {
        device: dev_name.to_string(),
        err: e,
    })?;

    let alsa_format = match format {
        AudioFormat::F64 => Format::float64(),
        AudioFormat::F32 => Format::float(),
        AudioFormat::S32 => Format::s32(),
        AudioFormat::S24 => Format::s24(),
        AudioFormat::S16 => Format::s16(),

        #[cfg(target_endian = "little")]
        AudioFormat::S24_3 => Format::S243LE,
        #[cfg(target_endian = "big")]
        AudioFormat::S24_3 => Format::S243BE,
    };

    let bytes_per_period = {
        let hwp = HwParams::any(&pcm).map_err(AlsaError::HwParams)?;
        hwp.set_access(Access::RWInterleaved)
            .map_err(|e| AlsaError::UnsupportedAccessType {
                device: dev_name.to_string(),
                err: e,
            })?;

        hwp.set_format(alsa_format)
            .map_err(|e| AlsaError::UnsupportedFormat {
                device: dev_name.to_string(),
                format,
                err: e,
            })?;

        hwp.set_rate(SAMPLE_RATE, ValueOr::Nearest).map_err(|e| {
            AlsaError::UnsupportedSampleRate {
                device: dev_name.to_string(),
                samplerate: SAMPLE_RATE,
                err: e,
            }
        })?;

        hwp.set_channels(NUM_CHANNELS as u32)
            .map_err(|e| AlsaError::UnsupportedChannelCount {
                device: dev_name.to_string(),
                channel_count: NUM_CHANNELS,
                err: e,
            })?;

        hwp.set_buffer_time_near(BUFFER_TIME.as_micros() as u32, ValueOr::Nearest)
            .map_err(AlsaError::HwParams)?;

        hwp.set_period_time_near(PERIOD_TIME.as_micros() as u32, ValueOr::Nearest)
            .map_err(AlsaError::HwParams)?;

        pcm.hw_params(&hwp).map_err(AlsaError::Pcm)?;

        let swp = pcm.sw_params_current().map_err(AlsaError::Pcm)?;

        // Don't assume we got what we wanted.
        // Ask to make sure.
        let frames_per_period = hwp.get_period_size().map_err(AlsaError::HwParams)?;

        let frames_per_buffer = hwp.get_buffer_size().map_err(AlsaError::HwParams)?;

        swp.set_start_threshold(frames_per_buffer - frames_per_period)
            .map_err(AlsaError::SwParams)?;

        pcm.sw_params(&swp).map_err(AlsaError::Pcm)?;

        trace!("Frames per Buffer: {:?}", frames_per_buffer);
        trace!("Frames per Period: {:?}", frames_per_period);

        // Let ALSA do the math for us.
        pcm.frames_to_bytes(frames_per_period) as usize
    };

    trace!("Period Buffer size in bytes: {:?}", bytes_per_period);

    Ok((pcm, bytes_per_period))
}

impl Open for AlsaSink {
    fn open(device: Option<String>, format: AudioFormat) -> Self {
        let name = match device.as_deref() {
            Some("?") => match list_outputs() {
                Ok(_) => {
                    exit(0);
                }
                Err(err) => {
                    error!("Error listing Alsa outputs, {}", err);
                    exit(1);
                }
            },
            Some(device) => device,
            None => "default",
        }
        .to_string();

        info!("Using AlsaSink with format: {:?}", format);

        Self {
            pcm: None,
            format,
            device: name,
            period_buffer: vec![],
        }
    }
}

impl Sink for AlsaSink {
    fn start(&mut self) -> io::Result<()> {
        if self.pcm.is_none() {
            match open_device(&self.device, self.format) {
                Ok((pcm, bytes_per_period)) => {
                    self.pcm = Some(pcm);
                    // If the capacity is greater than we want shrink it
                    // to it's current len (which should be zero) before
                    // setting the capacity with reserve_exact.
                    if self.period_buffer.capacity() > bytes_per_period {
                        self.period_buffer.shrink_to_fit();
                    }
                    // This does nothing if the capacity is already sufficient.
                    // Len should always be zero, but for the sake of being thorough...
                    self.period_buffer
                        .reserve_exact(bytes_per_period - self.period_buffer.len());

                    // Should always match the "Period Buffer size in bytes: " trace! message.
                    trace!(
                        "Period Buffer capacity: {:?}",
                        self.period_buffer.capacity()
                    );
                }
                Err(e) => {
                    return Err(io::Error::new(io::ErrorKind::Other, e));
                }
            }
        }

        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        // Zero fill the remainder of the period buffer and
        // write any leftover data before draining the actual PCM buffer.
        self.period_buffer.resize(self.period_buffer.capacity(), 0);
        self.write_buf()?;

        let pcm = self.pcm.as_mut().ok_or_else(|| {
            io::Error::new(io::ErrorKind::Other, "Error stopping AlsaSink, PCM is None")
        })?;

        pcm.drain().map_err(|e| {
            io::Error::new(
                io::ErrorKind::Other,
                format!("Error stopping AlsaSink {}", e),
            )
        })?;

        self.pcm = None;
        Ok(())
    }

    sink_as_bytes!();
}

impl SinkAsBytes for AlsaSink {
    fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> {
        let mut start_index = 0;
        let data_len = data.len();
        let capacity = self.period_buffer.capacity();
        loop {
            let data_left = data_len - start_index;
            let space_left = capacity - self.period_buffer.len();
            let data_to_buffer = min(data_left, space_left);
            let end_index = start_index + data_to_buffer;
            self.period_buffer
                .extend_from_slice(&data[start_index..end_index]);
            if self.period_buffer.len() == capacity {
                self.write_buf()?;
            }
            if end_index == data_len {
                break Ok(());
            }
            start_index = end_index;
        }
    }
}

impl AlsaSink {
    pub const NAME: &'static str = "alsa";

    fn write_buf(&mut self) -> io::Result<()> {
        let pcm = self.pcm.as_mut().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::Other,
                "Error writing from AlsaSink buffer to PCM, PCM is None",
            )
        })?;
        let io = pcm.io_bytes();
        if let Err(err) = io.writei(&self.period_buffer) {
            // Capture and log the original error as a warning, and then try to recover.
            // If recovery fails then forward that error back to player.
            warn!(
                "Error writing from AlsaSink buffer to PCM, trying to recover {}",
                err
            );
            pcm.try_recover(err, false).map_err(|e| {
                io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "Error writing from AlsaSink buffer to PCM, recovery failed {}",
                        e
                    ),
                )
            })?
        }

        self.period_buffer.clear();
        Ok(())
    }
}
<|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/. */

use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use core::clone::Clone;
use dom::bindings::codegen::Bindings::BluetoothBinding;
use dom::bindings::codegen::Bindings::BluetoothBinding::{BluetoothMethods, BluetoothScanFilter};
use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions;
use dom::bindings::error::Error::{self, Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetoothadvertisingdata::BluetoothAdvertisingData;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothuuid::BluetoothUUID;
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence};
use net_traits::bluetooth_scanfilter::{RequestDeviceoptions, ServiceUUIDSequence};
use net_traits::bluetooth_thread::{BluetoothError, BluetoothMethodMsg};

const FILTER_EMPTY_ERROR: &'static str = "'filters' member must be non - empty to find any devices.";
const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way.";
const FILTER_NAME_TOO_LONG_ERROR: &'static str = "A 'name' or 'namePrefix' can't be longer then 29 bytes.";
// 248 is the maximum number of UTF-8 code units in a Bluetooth Device Name.
const MAX_DEVICE_NAME_LENGTH: usize = 248;
// A device name can never be longer than 29 bytes.
// An advertising packet is at most 31 bytes long.
// The length and identifier of the length field take 2 bytes.
// That leaves 29 bytes for the name.
const MAX_FILTER_NAME_LENGTH: usize = 29;
const NAME_PREFIX_ERROR: &'static str = "'namePrefix', if present, must be non - empty.";
const NAME_TOO_LONG_ERROR: &'static str = "A device name can't be longer than 248 bytes.";
const SERVICE_ERROR: &'static str = "'services', if present, must contain at least one service.";

// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth
#[dom_struct]
pub struct Bluetooth {
    reflector_: Reflector,
}

impl Bluetooth {
    pub fn new_inherited() -> Bluetooth {
        Bluetooth {
            reflector_: Reflector::new(),
        }
    }

    pub fn new(global: GlobalRef) -> Root<Bluetooth> {
        reflect_dom_object(box Bluetooth::new_inherited(),
                           global,
                           BluetoothBinding::Wrap)
    }

    fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
        let global_root = self.global();
        let global_ref = global_root.r();
        global_ref.as_window().bluetooth_thread()
    }
}

fn canonicalize_filter(filter: &BluetoothScanFilter, global: GlobalRef) -> Fallible<BluetoothScanfilter> {
    if filter.services.is_none() && filter.name.is_none() && filter.namePrefix.is_none() {
        return Err(Type(FILTER_ERROR.to_owned()));
    }

    let mut services_vec = vec!();
    if let Some(ref services) = filter.services {
        if services.is_empty() {
            return Err(Type(SERVICE_ERROR.to_owned()));
        }
        for service in services {
            let uuid = try!(BluetoothUUID::GetService(global, service.clone())).to_string();
            if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                return Err(Security)
            }
            services_vec.push(uuid);
        }
    }

    let mut name = String::new();
    if let Some(ref filter_name) = filter.name {
        //NOTE: DOMString::len() gives back the size in bytes
        if filter_name.len() > MAX_DEVICE_NAME_LENGTH {
            return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
        }
        if filter_name.len() > MAX_FILTER_NAME_LENGTH {
            return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned()));
        }
        name = filter_name.to_string();
    }

    let mut name_prefix = String::new();
    if let Some(ref filter_name_prefix) = filter.namePrefix {
        if filter_name_prefix.is_empty() {
            return Err(Type(NAME_PREFIX_ERROR.to_owned()));
        }
        if filter_name_prefix.len() > MAX_DEVICE_NAME_LENGTH {
            return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
        }
        if filter_name_prefix.len() > MAX_FILTER_NAME_LENGTH {
            return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned()));
        }
        name_prefix = filter_name_prefix.to_string();
    }

    Ok(BluetoothScanfilter::new(name, name_prefix, services_vec))
}

fn convert_request_device_options(options: &RequestDeviceOptions,
                                  global: GlobalRef)
                                  -> Fallible<RequestDeviceoptions> {
    if options.filters.is_empty() {
        return Err(Type(FILTER_EMPTY_ERROR.to_owned()));
    }

    let mut filters = vec!();
    for filter in &options.filters {
        filters.push(try!(canonicalize_filter(&filter, global)));
    }

    let mut optional_services = vec!();
    if let Some(ref opt_services) = options.optionalServices {
        for opt_service in opt_services {
            let uuid = try!(BluetoothUUID::GetService(global, opt_service.clone())).to_string();
            if !uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                optional_services.push(uuid);
            }
        }
    }

    Ok(RequestDeviceoptions::new(BluetoothScanfilterSequence::new(filters),
                                 ServiceUUIDSequence::new(optional_services)))
}

impl From<BluetoothError> for Error {
    fn from(error: BluetoothError) -> Self {
        match error {
            BluetoothError::Type(message) => Error::Type(message),
            BluetoothError::Network => Error::Network,
            BluetoothError::NotFound => Error::NotFound,
            BluetoothError::NotSupported => Error::NotSupported,
            BluetoothError::Security => Error::Security,
        }
    }
}

impl BluetoothMethods for Bluetooth {
    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
    fn RequestDevice(&self, option: &RequestDeviceOptions) -> Fallible<Root<BluetoothDevice>> {
        let (sender, receiver) = ipc::channel().unwrap();
        let option = try!(convert_request_device_options(option, self.global().r()));
        self.get_bluetooth_thread().send(BluetoothMethodMsg::RequestDevice(option, sender)).unwrap();
        let device = receiver.recv().unwrap();
        match device {
            Ok(device) => {
                let ad_data = BluetoothAdvertisingData::new(self.global().r(),
                                                            device.appearance,
                                                            device.tx_power,
                                                            device.rssi);
                Ok(BluetoothDevice::new(self.global().r(),
                                        DOMString::from(device.id),
                                        device.name.map(DOMString::from),
                                        &ad_data))
            },
            Err(error) => {
                Err(Error::from(error))
            },
        }
    }
}
<commit_msg>requestDevice refactor<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/. */

use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
use core::clone::Clone;
use dom::bindings::codegen::Bindings::BluetoothBinding;
use dom::bindings::codegen::Bindings::BluetoothBinding::{BluetoothMethods, BluetoothScanFilter};
use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions;
use dom::bindings::error::Error::{self, Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetoothadvertisingdata::BluetoothAdvertisingData;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
use ipc_channel::ipc::{self, IpcSender};
use net_traits::bluetooth_scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence};
use net_traits::bluetooth_scanfilter::{RequestDeviceoptions, ServiceUUIDSequence};
use net_traits::bluetooth_thread::{BluetoothError, BluetoothMethodMsg};

const FILTER_EMPTY_ERROR: &'static str = "'filters' member must be nonempty to find any devices.";
const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way.";
const FILTER_NAME_TOO_LONG_ERROR: &'static str = "A 'name' or 'namePrefix' can't be longer then 29 bytes.";
// 248 is the maximum number of UTF-8 code units in a Bluetooth Device Name.
const MAX_DEVICE_NAME_LENGTH: usize = 248;
// A device name can never be longer than 29 bytes.
// An advertising packet is at most 31 bytes long.
// The length and identifier of the length field take 2 bytes.
// That leaves 29 bytes for the name.
const MAX_FILTER_NAME_LENGTH: usize = 29;
const NAME_PREFIX_ERROR: &'static str = "'namePrefix', if present, must be nonempty.";
const NAME_TOO_LONG_ERROR: &'static str = "A device name can't be longer than 248 bytes.";
const SERVICE_ERROR: &'static str = "'services', if present, must contain at least one service.";

// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth
#[dom_struct]
pub struct Bluetooth {
    reflector_: Reflector,
}

impl Bluetooth {
    pub fn new_inherited() -> Bluetooth {
        Bluetooth {
            reflector_: Reflector::new(),
        }
    }

    pub fn new(global: GlobalRef) -> Root<Bluetooth> {
        reflect_dom_object(box Bluetooth::new_inherited(),
                           global,
                           BluetoothBinding::Wrap)
    }

    fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
        let global_root = self.global();
        let global_ref = global_root.r();
        global_ref.as_window().bluetooth_thread()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
    fn request_bluetooth_devices(&self,
                                 filters: &[BluetoothScanFilter],
                                 optional_services: &Option<Vec<BluetoothServiceUUID>>)
                                 -> Fallible<Root<BluetoothDevice>> {
        let option = try!(convert_request_device_options(self.global().r(), filters, optional_services));
        let (sender, receiver) = ipc::channel().unwrap();
        self.get_bluetooth_thread().send(BluetoothMethodMsg::RequestDevice(option, sender)).unwrap();
        let device = receiver.recv().unwrap();
        match device {
            Ok(device) => {
                let ad_data = BluetoothAdvertisingData::new(self.global().r(),
                                                            device.appearance,
                                                            device.tx_power,
                                                            device.rssi);
                Ok(BluetoothDevice::new(self.global().r(),
                                        DOMString::from(device.id),
                                        device.name.map(DOMString::from),
                                        &ad_data))
            },
            Err(error) => {
                Err(Error::from(error))
            },
        }
    }
}

fn convert_request_device_options(global: GlobalRef,
                                  filters: &[BluetoothScanFilter],
                                  optional_services: &Option<Vec<BluetoothServiceUUID>>)
                                  -> Fallible<RequestDeviceoptions> {
    if filters.is_empty() {
        return Err(Type(FILTER_EMPTY_ERROR.to_owned()));
    }

    let mut filters_vec = vec!();
    for filter in filters {
        filters_vec.push(try!(canonicalize_filter(&filter, global)));
    }

    let mut optional_services_vec = vec!();
    if let &Some(ref opt_services) = optional_services {
        for opt_service in opt_services {
            let uuid = try!(BluetoothUUID::GetService(global, opt_service.clone())).to_string();
            if !uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                optional_services_vec.push(uuid);
            }
        }
    }

    Ok(RequestDeviceoptions::new(BluetoothScanfilterSequence::new(filters_vec),
                                 ServiceUUIDSequence::new(optional_services_vec)))
}

fn canonicalize_filter(filter: &BluetoothScanFilter, global: GlobalRef) -> Fallible<BluetoothScanfilter> {
    if filter.services.is_none() && filter.name.is_none() && filter.namePrefix.is_none() {
        return Err(Type(FILTER_ERROR.to_owned()));
    }

    let services_vec = match filter.services {
        Some(ref services) => {
            if services.is_empty() {
                return Err(Type(SERVICE_ERROR.to_owned()));
            }
            let mut services_vec = vec!();
            for service in services {
                let uuid = try!(BluetoothUUID::GetService(global, service.clone())).to_string();
                if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                    return Err(Security)
                }
                services_vec.push(uuid);
            }
            services_vec
        },
        None => vec!(),
    };

    let name = match filter.name {
        Some(ref name) => {
            // Note: DOMString::len() gives back the size in bytes.
            if name.len() > MAX_DEVICE_NAME_LENGTH {
                return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
            }
            if name.len() > MAX_FILTER_NAME_LENGTH {
                return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned()));
            }
            name.to_string()
        },
        None => String::new(),
    };

    let name_prefix = match filter.namePrefix {
        Some(ref name_prefix) => {
            if name_prefix.is_empty() {
                return Err(Type(NAME_PREFIX_ERROR.to_owned()));
            }
            if name_prefix.len() > MAX_DEVICE_NAME_LENGTH {
                return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
            }
            if name_prefix.len() > MAX_FILTER_NAME_LENGTH {
                return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned()));
            }
            name_prefix.to_string()
        },
        None => String::new(),
    };

    Ok(BluetoothScanfilter::new(name, name_prefix, services_vec))
}

impl From<BluetoothError> for Error {
    fn from(error: BluetoothError) -> Self {
        match error {
            BluetoothError::Type(message) => Error::Type(message),
            BluetoothError::Network => Error::Network,
            BluetoothError::NotFound => Error::NotFound,
            BluetoothError::NotSupported => Error::NotSupported,
            BluetoothError::Security => Error::Security,
        }
    }
}

impl BluetoothMethods for Bluetooth {
    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
    fn RequestDevice(&self, option: &RequestDeviceOptions) -> Fallible<Root<BluetoothDevice>> {
        self.request_bluetooth_devices(&option.filters, &option.optionalServices)
    }
}
<|endoftext|>


<commit_before>use std::io;

pub struct State(imp::State);

impl State {
    /// Captures the current state of all CPUs on the system.
    ///
    /// The `State` returned here isn't too meaningful in terms of
    /// interpretation across platforms, but it can be compared to previous
    /// states to get a meaningful cross-platform number.
    pub fn current() -> io::Result<State> {
        imp::current().map(State)
    }

    /// Returns the percentage of time CPUs were idle from the current state
    /// relative to the previous state, as a percentage from 0.0 to 100.0.
    ///
    /// This function will return, as a percentage, the amount of time that the
    /// entire system was idle between the `previous` state and this own state.
    /// This can be useful to compare two snapshots in time of CPU usage to see
    /// how the CPU usage compares between the two.
    pub fn idle_since(&self, previous: &State) -> f64 {
        imp::pct_idle(&previous.0, &self.0)
    }
}

#[cfg(target_os = "linux")]
mod imp {
    use std::fs::File;
    use std::io::{self, Read};

    pub struct State {
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
    }

    pub fn current() -> io::Result<State> {
        let mut state = String::new();
        File::open("/proc/stat")?.read_to_string(&mut state)?;
        let mut parts = state.lines().next().unwrap().split_whitespace();
        if parts.next() != Some("cpu") {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "cannot parse /proc/stat",
            ));
        }

        Ok(State {
            user: parts.next().unwrap().parse::<u64>().unwrap(),
            nice: parts.next().unwrap().parse::<u64>().unwrap(),
            system: parts.next().unwrap().parse::<u64>().unwrap(),
            idle: parts.next().unwrap().parse::<u64>().unwrap(),
            iowait: parts.next().unwrap().parse::<u64>().unwrap(),
            irq: parts.next().unwrap().parse::<u64>().unwrap(),
            softirq: parts.next().unwrap().parse::<u64>().unwrap(),
            steal: parts.next().unwrap().parse::<u64>().unwrap(),
            guest: parts.next().unwrap().parse::<u64>().unwrap(),
            guest_nice: parts.next().unwrap().parse::<u64>().unwrap(),
        })
    }

    pub fn pct_idle(prev: &State, next: &State) -> f64 {
        let user = next.user - prev.user;
        let nice = next.nice - prev.nice;
        let system = next.system - prev.system;
        let idle = next.idle - prev.idle;
        let iowait = next.iowait - prev.iowait;
        let irq = next.irq - prev.irq;
        let softirq = next.softirq - prev.softirq;
        let steal = next.steal - prev.steal;
        let guest = next.guest - prev.guest;
        let guest_nice = next.guest_nice - prev.guest_nice;
        let total =
            user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice;

        (idle as f64) / (total as f64) * 100.0
    }
}

#[cfg(target_os = "macos")]
#[allow(bad_style)]
mod imp {
    use std::io;
    use std::ptr;

    type host_t = u32;
    type mach_port_t = u32;
    type vm_map_t = mach_port_t;
    type vm_offset_t = usize;
    type vm_size_t = usize;
    type vm_address_t = vm_offset_t;
    type processor_flavor_t = i32;
    type natural_t = u32;
    type processor_info_array_t = *mut i32;
    type mach_msg_type_number_t = i32;
    type kern_return_t = i32;

    const PROESSOR_CPU_LOAD_INFO: processor_flavor_t = 2;
    const CPU_STATE_USER: usize = 0;
    const CPU_STATE_SYSTEM: usize = 1;
    const CPU_STATE_IDLE: usize = 2;
    const CPU_STATE_NICE: usize = 3;
    const CPU_STATE_MAX: usize = 4;

    extern "C" {
        static mut mach_task_self_: mach_port_t;

        fn mach_host_self() -> mach_port_t;
        fn host_processor_info(
            host: host_t,
            flavor: processor_flavor_t,
            out_processor_count: *mut natural_t,
            out_processor_info: *mut processor_info_array_t,
            out_processor_infoCnt: *mut mach_msg_type_number_t,
        ) -> kern_return_t;
        fn vm_deallocate(
            target_task: vm_map_t,
            address: vm_address_t,
            size: vm_size_t,
        ) -> kern_return_t;
    }

    pub struct State {
        user: u64,
        system: u64,
        idle: u64,
        nice: u64,
    }

    #[repr(C)]
    struct processor_cpu_load_info_data_t {
        cpu_ticks: [u32; CPU_STATE_MAX],
    }

    pub fn current() -> io::Result<State> {
        // There's scant little documentation on `host_processor_info`
        // throughout the internet, so this is just modeled after what everyone
        // else is doing. For now this is modeled largely after libuv.

        unsafe {
            let mut num_cpus_u = 0;
            let mut cpu_info = ptr::null_mut();
            let mut msg_type = 0;
            let err = host_processor_info(
                mach_host_self(),
                PROESSOR_CPU_LOAD_INFO,
                &mut num_cpus_u,
                &mut cpu_info,
                &mut msg_type,
            );
            if err != 0 {
                return Err(io::Error::last_os_error());
            }
            let mut ret = State {
                user: 0,
                system: 0,
                idle: 0,
                nice: 0,
            };
            let mut current = cpu_info as *const processor_cpu_load_info_data_t;
            for _ in 0..num_cpus_u {
                ret.user += (*current).cpu_ticks[CPU_STATE_USER] as u64;
                ret.system += (*current).cpu_ticks[CPU_STATE_SYSTEM] as u64;
                ret.idle += (*current).cpu_ticks[CPU_STATE_IDLE] as u64;
                ret.nice += (*current).cpu_ticks[CPU_STATE_NICE] as u64;
                current = current.offset(1);
            }
            vm_deallocate(mach_task_self_, cpu_info as vm_address_t, msg_type as usize);
            Ok(ret)
        }
    }

    pub fn pct_idle(prev: &State, next: &State) -> f64 {
        let user = next.user - prev.user;
        let system = next.system - prev.system;
        let idle = next.idle - prev.idle;
        let nice = next.nice - prev.nice;
        let total = user + system + idle + nice;
        (idle as f64) / (total as f64) * 100.0
    }
}

#[cfg(windows)]
mod imp {
    use std::io;
    use std::mem;
    use winapi::shared::minwindef::*;
    use winapi::um::processthreadsapi::*;

    pub struct State {
        idle: FILETIME,
        kernel: FILETIME,
        user: FILETIME,
    }

    pub fn current() -> io::Result<State> {
        unsafe {
            let mut ret = mem::zeroed::<State>();
            let r = GetSystemTimes(&mut ret.idle, &mut ret.kernel, &mut ret.user);
            if r != 0 {
                Ok(ret)
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }

    pub fn pct_idle(prev: &State, next: &State) -> f64 {
        fn to_u64(a: &FILETIME) -> u64 {
            ((a.dwHighDateTime as u64) << 32) | (a.dwLowDateTime as u64)
        }

        let idle = to_u64(&next.idle) - to_u64(&prev.idle);
        let kernel = to_u64(&next.kernel) - to_u64(&prev.kernel);
        let user = to_u64(&next.user) - to_u64(&prev.user);
        let total = user + kernel;
        (idle as f64) / (total as f64) * 100.0
    }
}

#[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
mod imp {
    use std::io;

    pub struct State;

    pub fn current() -> io::Result<State> {
        Err(io::Error::new(
            io::ErrorKind::Other,
            "unsupported platform to learn CPU state",
        ))
    }

    pub fn pct_idle(_prev: &State, _next: &State) -> f64 {
        unimplemented!()
    }
}
<commit_msg>Don't panic when parsing `/proc/stat`<commit_after>use std::io;

pub struct State(imp::State);

impl State {
    /// Captures the current state of all CPUs on the system.
    ///
    /// The `State` returned here isn't too meaningful in terms of
    /// interpretation across platforms, but it can be compared to previous
    /// states to get a meaningful cross-platform number.
    pub fn current() -> io::Result<State> {
        imp::current().map(State)
    }

    /// Returns the percentage of time CPUs were idle from the current state
    /// relative to the previous state, as a percentage from 0.0 to 100.0.
    ///
    /// This function will return, as a percentage, the amount of time that the
    /// entire system was idle between the `previous` state and this own state.
    /// This can be useful to compare two snapshots in time of CPU usage to see
    /// how the CPU usage compares between the two.
    pub fn idle_since(&self, previous: &State) -> f64 {
        imp::pct_idle(&previous.0, &self.0)
    }
}

#[cfg(target_os = "linux")]
mod imp {
    use std::fs::File;
    use std::io::{self, Read};

    pub struct State {
        user: u64,
        nice: u64,
        system: u64,
        idle: u64,
        iowait: u64,
        irq: u64,
        softirq: u64,
        steal: u64,
        guest: u64,
        guest_nice: u64,
    }

    pub fn current() -> io::Result<State> {
        let mut state = String::new();
        File::open("/proc/stat")?.read_to_string(&mut state)?;

        (|| {
            let mut parts = state.lines().next()?.split_whitespace();
            if parts.next()? != "cpu" {
                return None;
            }
            Some(State {
                user: parts.next()?.parse::<u64>().ok()?,
                nice: parts.next()?.parse::<u64>().ok()?,
                system: parts.next()?.parse::<u64>().ok()?,
                idle: parts.next()?.parse::<u64>().ok()?,
                iowait: parts.next()?.parse::<u64>().ok()?,
                irq: parts.next()?.parse::<u64>().ok()?,
                softirq: parts.next()?.parse::<u64>().ok()?,
                steal: parts.next()?.parse::<u64>().ok()?,
                guest: parts.next()?.parse::<u64>().ok()?,
                guest_nice: parts.next()?.parse::<u64>().ok()?,
            })
        })()
        .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "first line of /proc/stat malformed"))
    }

    pub fn pct_idle(prev: &State, next: &State) -> f64 {
        let user = next.user - prev.user;
        let nice = next.nice - prev.nice;
        let system = next.system - prev.system;
        let idle = next.idle - prev.idle;
        let iowait = next.iowait - prev.iowait;
        let irq = next.irq - prev.irq;
        let softirq = next.softirq - prev.softirq;
        let steal = next.steal - prev.steal;
        let guest = next.guest - prev.guest;
        let guest_nice = next.guest_nice - prev.guest_nice;
        let total =
            user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice;

        (idle as f64) / (total as f64) * 100.0
    }
}

#[cfg(target_os = "macos")]
#[allow(bad_style)]
mod imp {
    use std::io;
    use std::ptr;

    type host_t = u32;
    type mach_port_t = u32;
    type vm_map_t = mach_port_t;
    type vm_offset_t = usize;
    type vm_size_t = usize;
    type vm_address_t = vm_offset_t;
    type processor_flavor_t = i32;
    type natural_t = u32;
    type processor_info_array_t = *mut i32;
    type mach_msg_type_number_t = i32;
    type kern_return_t = i32;

    const PROESSOR_CPU_LOAD_INFO: processor_flavor_t = 2;
    const CPU_STATE_USER: usize = 0;
    const CPU_STATE_SYSTEM: usize = 1;
    const CPU_STATE_IDLE: usize = 2;
    const CPU_STATE_NICE: usize = 3;
    const CPU_STATE_MAX: usize = 4;

    extern "C" {
        static mut mach_task_self_: mach_port_t;

        fn mach_host_self() -> mach_port_t;
        fn host_processor_info(
            host: host_t,
            flavor: processor_flavor_t,
            out_processor_count: *mut natural_t,
            out_processor_info: *mut processor_info_array_t,
            out_processor_infoCnt: *mut mach_msg_type_number_t,
        ) -> kern_return_t;
        fn vm_deallocate(
            target_task: vm_map_t,
            address: vm_address_t,
            size: vm_size_t,
        ) -> kern_return_t;
    }

    pub struct State {
        user: u64,
        system: u64,
        idle: u64,
        nice: u64,
    }

    #[repr(C)]
    struct processor_cpu_load_info_data_t {
        cpu_ticks: [u32; CPU_STATE_MAX],
    }

    pub fn current() -> io::Result<State> {
        // There's scant little documentation on `host_processor_info`
        // throughout the internet, so this is just modeled after what everyone
        // else is doing. For now this is modeled largely after libuv.

        unsafe {
            let mut num_cpus_u = 0;
            let mut cpu_info = ptr::null_mut();
            let mut msg_type = 0;
            let err = host_processor_info(
                mach_host_self(),
                PROESSOR_CPU_LOAD_INFO,
                &mut num_cpus_u,
                &mut cpu_info,
                &mut msg_type,
            );
            if err != 0 {
                return Err(io::Error::last_os_error());
            }
            let mut ret = State {
                user: 0,
                system: 0,
                idle: 0,
                nice: 0,
            };
            let mut current = cpu_info as *const processor_cpu_load_info_data_t;
            for _ in 0..num_cpus_u {
                ret.user += (*current).cpu_ticks[CPU_STATE_USER] as u64;
                ret.system += (*current).cpu_ticks[CPU_STATE_SYSTEM] as u64;
                ret.idle += (*current).cpu_ticks[CPU_STATE_IDLE] as u64;
                ret.nice += (*current).cpu_ticks[CPU_STATE_NICE] as u64;
                current = current.offset(1);
            }
            vm_deallocate(mach_task_self_, cpu_info as vm_address_t, msg_type as usize);
            Ok(ret)
        }
    }

    pub fn pct_idle(prev: &State, next: &State) -> f64 {
        let user = next.user - prev.user;
        let system = next.system - prev.system;
        let idle = next.idle - prev.idle;
        let nice = next.nice - prev.nice;
        let total = user + system + idle + nice;
        (idle as f64) / (total as f64) * 100.0
    }
}

#[cfg(windows)]
mod imp {
    use std::io;
    use std::mem;
    use winapi::shared::minwindef::*;
    use winapi::um::processthreadsapi::*;

    pub struct State {
        idle: FILETIME,
        kernel: FILETIME,
        user: FILETIME,
    }

    pub fn current() -> io::Result<State> {
        unsafe {
            let mut ret = mem::zeroed::<State>();
            let r = GetSystemTimes(&mut ret.idle, &mut ret.kernel, &mut ret.user);
            if r != 0 {
                Ok(ret)
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }

    pub fn pct_idle(prev: &State, next: &State) -> f64 {
        fn to_u64(a: &FILETIME) -> u64 {
            ((a.dwHighDateTime as u64) << 32) | (a.dwLowDateTime as u64)
        }

        let idle = to_u64(&next.idle) - to_u64(&prev.idle);
        let kernel = to_u64(&next.kernel) - to_u64(&prev.kernel);
        let user = to_u64(&next.user) - to_u64(&prev.user);
        let total = user + kernel;
        (idle as f64) / (total as f64) * 100.0
    }
}

#[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
mod imp {
    use std::io;

    pub struct State;

    pub fn current() -> io::Result<State> {
        Err(io::Error::new(
            io::ErrorKind::Other,
            "unsupported platform to learn CPU state",
        ))
    }

    pub fn pct_idle(_prev: &State, _next: &State) -> f64 {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use analysis;
use library;
use chunk::Chunk;
use consts::TYPE_PARAMETERS_START;
use env::Env;
use super::general::{cfg_deprecated, doc_hidden, version_condition};
use super::signal_body;
use super::trampoline::func_string;
use writer::primitives::tabs;
use writer::ToCode;

#[cfg_attr(feature = "cargo-clippy", allow(ptr_arg))]
pub fn generate(
    w: &mut Write,
    env: &Env,
    analysis: &analysis::signals::Info,
    trampolines: &analysis::trampolines::Trampolines,
    in_trait: bool,
    only_declaration: bool,
    indent: usize,
) -> Result<()> {
    let commented = analysis.trampoline_name.is_err();
    let comment_prefix = if commented { "//" } else { "" };
    let pub_prefix = if in_trait { "" } else { "pub " };

    let function_type = function_type_string(env, analysis, trampolines, true);
    let declaration = declaration(analysis, &function_type);
    let suffix = if only_declaration { ";" } else { " {" };

    try!(writeln!(w));
    if !in_trait || only_declaration {
        try!(cfg_deprecated(w, env, analysis.deprecated_version, commented, indent));
    }
    try!(version_condition(
        w,
        env,
        analysis.version,
        commented,
        indent,
    ));
    try!(doc_hidden(w, analysis.doc_hidden, comment_prefix, indent));
    try!(writeln!(
        w,
        "{}{}{}{}{}",
        tabs(indent),
        comment_prefix,
        pub_prefix,
        declaration,
        suffix
    ));

    if !only_declaration {
        match function_type {
            Some(ref type_) => {
                let body = body(analysis, type_, in_trait).to_code(env);
                for s in body {
                    try!(writeln!(w, "{}{}", tabs(indent), s));
                }
            }
            _ => if let Err(ref errors) = analysis.trampoline_name {
                for error in errors {
                    try!(writeln!(w, "{}{}\t{}", tabs(indent), comment_prefix, error));
                }
                try!(writeln!(w, "{}{}}}", tabs(indent), comment_prefix));
            } else {
                try!(writeln!(
                    w,
                    "{}{}\tTODO: connect to trampoline\n{0}{1}}}",
                    tabs(indent),
                    comment_prefix
                ));
            },
        }
    }

    if let Some(ref emit_name) = analysis.action_emit_name {
        try!(writeln!(w));
        if !in_trait || only_declaration {
            try!(cfg_deprecated(w, env, analysis.deprecated_version, commented, indent));
        }
        try!(version_condition(
            w,
            env,
            analysis.version,
            commented,
            indent,
        ));

        let function_type = function_type_string(env, analysis, trampolines, false);

        try!(writeln!(
            w,
            "{}{}{}fn {}{}{}",
            tabs(indent),
            comment_prefix,
            pub_prefix,
            emit_name,
            function_type.unwrap(),
            suffix
        ));

        if !only_declaration {
            let trampoline_name = analysis.trampoline_name.as_ref().unwrap();
            let trampoline = match trampolines.iter().find(|t| *trampoline_name == t.name) {
                Some(trampoline) => trampoline,
                None => panic!(
                    "Internal error: can't find trampoline '{}'",
                    trampoline_name
                ),
            };
            let mut args = String::with_capacity(100);

            for (pos, par) in trampoline.parameters.rust_parameters.iter().enumerate() {
                // Skip the self parameter
                if pos == 0 {
                    continue;
                }

                if pos > 1 {
                    args.push_str(", ");
                }
                args.push('&');
                args.push_str(&par.name);
            }

            try!(writeln!(
                w,
                "{}let {} = self.emit(\"{}\", &[{}]).unwrap();",
                tabs(indent + 1),
                if trampoline.ret.typ != Default::default() {
                    "res"
                } else {
                    "_"
                },
                analysis.signal_name,
                args,
            ));

            if trampoline.ret.typ != Default::default() {
                if trampoline.ret.nullable == library::Nullable(true) {
                    try!(writeln!(w, "{}res.unwrap().get()", tabs(indent + 1),));
                } else {
                    try!(writeln!(
                        w,
                        "{}res.unwrap().get().unwrap()",
                        tabs(indent + 1),
                    ));
                }
            }
            try!(writeln!(w, "{}}}", tabs(indent)));
        }
    }

    Ok(())
}

#[cfg_attr(feature = "cargo-clippy", allow(ptr_arg))]
fn function_type_string(
    env: &Env,
    analysis: &analysis::signals::Info,
    trampolines: &analysis::trampolines::Trampolines,
    closure: bool,
) -> Option<String> {
    if analysis.trampoline_name.is_err() {
        return None;
    }

    let trampoline_name = analysis.trampoline_name.as_ref().unwrap();
    let trampoline = match trampolines.iter().find(|t| *trampoline_name == t.name) {
        Some(trampoline) => trampoline,
        None => panic!(
            "Internal error: can't find trampoline '{}'",
            trampoline_name
        ),
    };

    let type_ = func_string(
        env,
        trampoline,
        if closure {
            Some((TYPE_PARAMETERS_START, "Self"))
        } else {
            Some((TYPE_PARAMETERS_START, "self"))
        },
        closure,
    );
    Some(type_)
}

fn declaration(analysis: &analysis::signals::Info, function_type: &Option<String>) -> String {
    let bounds = bounds(function_type);
    let param_str = "&self, f: F";
    let return_str = " -> SignalHandlerId";
    format!(
        "fn {}<{}>({}){}",
        analysis.connect_name,
        bounds,
        param_str,
        return_str
    )
}

fn bounds(function_type: &Option<String>) -> String {
    match *function_type {
        Some(ref type_) => format!("F: {}", type_),
        _ => "Unsupported or ignored types".to_owned(),
    }
}

fn body(analysis: &analysis::signals::Info, function_type: &str, in_trait: bool) -> Chunk {
    let mut builder = signal_body::Builder::new();

    builder
        .signal_name(&analysis.signal_name)
        .trampoline_name(analysis.trampoline_name.as_ref().unwrap())
        .in_trait(in_trait)
        .function_type_string(function_type);

    builder.generate()
}
<commit_msg>Fix panic in generating `emit` for incomplete signal<commit_after>use std::io::{Result, Write};

use analysis;
use library;
use chunk::Chunk;
use consts::TYPE_PARAMETERS_START;
use env::Env;
use super::general::{cfg_deprecated, doc_hidden, version_condition};
use super::signal_body;
use super::trampoline::func_string;
use writer::primitives::tabs;
use writer::ToCode;

#[cfg_attr(feature = "cargo-clippy", allow(ptr_arg))]
pub fn generate(
    w: &mut Write,
    env: &Env,
    analysis: &analysis::signals::Info,
    trampolines: &analysis::trampolines::Trampolines,
    in_trait: bool,
    only_declaration: bool,
    indent: usize,
) -> Result<()> {
    let commented = analysis.trampoline_name.is_err();
    let comment_prefix = if commented { "//" } else { "" };
    let pub_prefix = if in_trait { "" } else { "pub " };

    let function_type = function_type_string(env, analysis, trampolines, true);
    let declaration = declaration(analysis, &function_type);
    let suffix = if only_declaration { ";" } else { " {" };

    try!(writeln!(w));
    if !in_trait || only_declaration {
        try!(cfg_deprecated(w, env, analysis.deprecated_version, commented, indent));
    }
    try!(version_condition(
        w,
        env,
        analysis.version,
        commented,
        indent,
    ));
    try!(doc_hidden(w, analysis.doc_hidden, comment_prefix, indent));
    try!(writeln!(
        w,
        "{}{}{}{}{}",
        tabs(indent),
        comment_prefix,
        pub_prefix,
        declaration,
        suffix
    ));

    if !only_declaration {
        match function_type {
            Some(ref type_) => {
                let body = body(analysis, type_, in_trait).to_code(env);
                for s in body {
                    try!(writeln!(w, "{}{}", tabs(indent), s));
                }
            }
            _ => if let Err(ref errors) = analysis.trampoline_name {
                for error in errors {
                    try!(writeln!(w, "{}{}\t{}", tabs(indent), comment_prefix, error));
                }
                try!(writeln!(w, "{}{}}}", tabs(indent), comment_prefix));
            } else {
                try!(writeln!(
                    w,
                    "{}{}\tTODO: connect to trampoline\n{0}{1}}}",
                    tabs(indent),
                    comment_prefix
                ));
            },
        }
    }

    if function_type.is_none() {
        // Signal incomplete, can't generate emit
        return Ok(());
    }

    if let Some(ref emit_name) = analysis.action_emit_name {
        try!(writeln!(w));
        if !in_trait || only_declaration {
            try!(cfg_deprecated(w, env, analysis.deprecated_version, commented, indent));
        }
        try!(version_condition(
            w,
            env,
            analysis.version,
            commented,
            indent,
        ));

        let function_type = function_type_string(env, analysis, trampolines, false);

        try!(writeln!(
            w,
            "{}{}{}fn {}{}{}",
            tabs(indent),
            comment_prefix,
            pub_prefix,
            emit_name,
            function_type.unwrap(),
            suffix
        ));

        if !only_declaration {
            let trampoline_name = analysis.trampoline_name.as_ref().unwrap();
            let trampoline = match trampolines.iter().find(|t| *trampoline_name == t.name) {
                Some(trampoline) => trampoline,
                None => panic!(
                    "Internal error: can't find trampoline '{}'",
                    trampoline_name
                ),
            };
            let mut args = String::with_capacity(100);

            for (pos, par) in trampoline.parameters.rust_parameters.iter().enumerate() {
                // Skip the self parameter
                if pos == 0 {
                    continue;
                }

                if pos > 1 {
                    args.push_str(", ");
                }
                args.push('&');
                args.push_str(&par.name);
            }

            try!(writeln!(
                w,
                "{}let {} = self.emit(\"{}\", &[{}]).unwrap();",
                tabs(indent + 1),
                if trampoline.ret.typ != Default::default() {
                    "res"
                } else {
                    "_"
                },
                analysis.signal_name,
                args,
            ));

            if trampoline.ret.typ != Default::default() {
                if trampoline.ret.nullable == library::Nullable(true) {
                    try!(writeln!(w, "{}res.unwrap().get()", tabs(indent + 1),));
                } else {
                    try!(writeln!(
                        w,
                        "{}res.unwrap().get().unwrap()",
                        tabs(indent + 1),
                    ));
                }
            }
            try!(writeln!(w, "{}}}", tabs(indent)));
        }
    }

    Ok(())
}

#[cfg_attr(feature = "cargo-clippy", allow(ptr_arg))]
fn function_type_string(
    env: &Env,
    analysis: &analysis::signals::Info,
    trampolines: &analysis::trampolines::Trampolines,
    closure: bool,
) -> Option<String> {
    if analysis.trampoline_name.is_err() {
        return None;
    }

    let trampoline_name = analysis.trampoline_name.as_ref().unwrap();
    let trampoline = match trampolines.iter().find(|t| *trampoline_name == t.name) {
        Some(trampoline) => trampoline,
        None => panic!(
            "Internal error: can't find trampoline '{}'",
            trampoline_name
        ),
    };

    let type_ = func_string(
        env,
        trampoline,
        if closure {
            Some((TYPE_PARAMETERS_START, "Self"))
        } else {
            Some((TYPE_PARAMETERS_START, "self"))
        },
        closure,
    );
    Some(type_)
}

fn declaration(analysis: &analysis::signals::Info, function_type: &Option<String>) -> String {
    let bounds = bounds(function_type);
    let param_str = "&self, f: F";
    let return_str = " -> SignalHandlerId";
    format!(
        "fn {}<{}>({}){}",
        analysis.connect_name,
        bounds,
        param_str,
        return_str
    )
}

fn bounds(function_type: &Option<String>) -> String {
    match *function_type {
        Some(ref type_) => format!("F: {}", type_),
        _ => "Unsupported or ignored types".to_owned(),
    }
}

fn body(analysis: &analysis::signals::Info, function_type: &str, in_trait: bool) -> Chunk {
    let mut builder = signal_body::Builder::new();

    builder
        .signal_name(&analysis.signal_name)
        .trampoline_name(analysis.trampoline_name.as_ref().unwrap())
        .in_trait(in_trait)
        .function_type_string(function_type);

    builder.generate()
}
<|endoftext|>


<commit_before><commit_msg>oak_utils: add compile_protos_to()<commit_after><|endoftext|>


<commit_before>
use assembler::data_range::DataRange;

enum Record<'a> {
  /// A data record. Supported in i8, i16 and i32.
  Data { address: u16, value: &'a [u8] },
  /// An end-of-file record. Supported in i8, i16 and i32.
  EndOfFile
}

mod type_codes {
  /// Type code for a Data record.
  pub const DATA_TYPE_CODE: u8 = 0x00;
  /// Type code for an End-of-File record.
  pub const END_OF_FILE_TYPE_CODE: u8 = 0x01;
}

impl<'a> Record<'a> {
  /**
   @return String value of the Intel HEX record for the receiver, excluding a trailing newline.
   */
  pub fn as_formatted_string(&self) -> String {
    match self {
      &Record::Data {address, value} => format_record(address, value, type_codes::DATA_TYPE_CODE),
      &Record::EndOfFile             => format_record(0x0000 , &[],   type_codes::END_OF_FILE_TYPE_CODE)
    }
  }
}

/**
 An Intel HEX record is composed of the following elements:
 +----------------+-----------------+---------------+-----------+---------+---------------+
 | Start Code (:) | Byte Count (u8) | Address (u16) | Type (u8) | Data... | Checksum (u8) |
 +----------------+-----------------+---------------+-----------+---------+---------------+
 */
fn format_record<'a>(address: u16, data: &'a [u8], type_code: u8) -> String {
  assert!(data.len() <= 255);

  // Construct the checksum region.
  let length_of_checksum_region = 4 + data.len();
  let mut checksum_region = Vec::<u8>::with_capacity(length_of_checksum_region);
  checksum_region.push(data.len() as u8);
  checksum_region.push(((address & 0xFF00) >> 8) as u8);
  checksum_region.push(((address & 0x00FF) >> 0) as u8);
  checksum_region.extend_from_slice(data);
  let checksum = ihex_checksum(checksum_region.as_slice());

  // Construct the record, excluding the start code and checksum.
  let checksum_region_string =
    checksum_region
      .iter()
      .map(|byte: &u8| format!("{:02X}", byte))
      .fold(String::new(), |acc, value| acc + &value);

  // Construct the formatted version of the record.
  format!(":{}{:02X}", checksum_region_string, checksum)
}

/**
 The Intel HEX checksum is computed as the two's complement of the sum of all
 elements preceding, excluding the Start Code. This is the Byte Count, Address,
 Record Type and Data.
 @param data Slice view of the checksum region.
 @return The Intel HEX checksum of the supplied region.
 */
fn ihex_checksum<'a>(data: &'a [u8]) -> u8 {
  let sum: usize = data.iter().fold(0, |acc, &value| acc.wrapping_add(value as usize));
  let last_byte_inverted = ((sum & 0xFF) as u8) ^ 0xFF;
  last_byte_inverted.wrapping_add(1)
}

// MARK: - Tests

#[cfg(test)]
mod tests {

  use super::*;
  use super::ihex_checksum;

  #[test]
  fn test_ihex_checksum() {
    assert_eq!(ihex_checksum(&vec![0x10,0x01,0x00,0x00,0x21,0x46,0x01,0x36,0x01,0x21,0x47,0x01,0x36,0x00,0x7E,0xFE,0x09,0xD2,0x19,0x01]), 0x40);
    assert_eq!(ihex_checksum(&vec![0x10,0x01,0x10,0x00,0x21,0x46,0x01,0x7E,0x17,0xC2,0x00,0x01,0xFF,0x5F,0x16,0x00,0x21,0x48,0x01,0x19]), 0x28);
    assert_eq!(ihex_checksum(&vec![0x10,0x01,0x20,0x00,0x19,0x4E,0x79,0x23,0x46,0x23,0x96,0x57,0x78,0x23,0x9E,0xDA,0x3F,0x01,0xB2,0xCA]), 0xA7);
    assert_eq!(ihex_checksum(&vec![0x10,0x01,0x30,0x00,0x3F,0x01,0x56,0x70,0x2B,0x5E,0x71,0x2B,0x72,0x2B,0x73,0x21,0x46,0x01,0x34,0x21]), 0xC7);
    assert_eq!(ihex_checksum(&vec![0x00,0x00,0x00,0x01]), 0xFF);
  }

}
<commit_msg>Fix format_record and add tests.<commit_after>
use assembler::data_range::DataRange;

enum Record<'a> {
  /// A data record. Supported in i8, i16 and i32.
  Data { address: u16, value: &'a [u8] },
  /// An end-of-file record. Supported in i8, i16 and i32.
  EndOfFile
}

mod type_codes {
  /// Type code for a Data record.
  pub const DATA_TYPE_CODE: u8 = 0x00;
  /// Type code for an End-of-File record.
  pub const END_OF_FILE_TYPE_CODE: u8 = 0x01;
}

impl<'a> Record<'a> {
  /**
   @return String value of the Intel HEX record for the receiver, excluding a trailing newline.
   */
  pub fn as_formatted_string(&self) -> String {
    match self {
      &Record::Data {address, value} => format_record(address, value, type_codes::DATA_TYPE_CODE),
      &Record::EndOfFile             => format_record(0x0000 , &[],   type_codes::END_OF_FILE_TYPE_CODE)
    }
  }
}

/**
 An Intel HEX record is composed of the following elements:
 +----------------+-----------------+---------------+-----------+---------+---------------+
 | Start Code (:) | Byte Count (u8) | Address (u16) | Type (u8) | Data... | Checksum (u8) |
 +----------------+-----------------+---------------+-----------+---------+---------------+
 */
fn format_record<'a>(address: u16, data: &'a [u8], type_code: u8) -> String {
  assert!(data.len() <= 255);

  // Construct the checksum region.
  let length_of_checksum_region = 4 + data.len();
  let mut checksum_region = Vec::<u8>::with_capacity(length_of_checksum_region);
  checksum_region.push(data.len() as u8);
  checksum_region.push(((address & 0xFF00) >> 8) as u8);
  checksum_region.push(((address & 0x00FF) >> 0) as u8);
  checksum_region.push(type_code);
  checksum_region.extend_from_slice(data);
  let checksum = ihex_checksum(checksum_region.as_slice());

  // Construct the record, excluding the start code and checksum.
  let checksum_region_string =
    checksum_region
      .iter()
      .map(|byte: &u8| format!("{:02X}", byte))
      .fold(String::new(), |acc, value| acc + &value);

  // Construct the formatted version of the record.
  format!(":{}{:02X}", checksum_region_string, checksum)
}

/**
 The Intel HEX checksum is computed as the two's complement of the sum of all
 elements preceding, excluding the Start Code. This is the Byte Count, Address,
 Record Type and Data.
 @param data Slice view of the checksum region.
 @return The Intel HEX checksum of the supplied region.
 */
fn ihex_checksum<'a>(data: &'a [u8]) -> u8 {
  let sum: usize = data.iter().fold(0, |acc, &value| acc.wrapping_add(value as usize));
  (((sum & 0xFF) as u8) ^ 0xFF).wrapping_add(1)
}

// MARK: - Tests

#[cfg(test)]
mod tests {

  use super::*;
  use super::{format_record, ihex_checksum, type_codes};

  #[test]
  fn test_ihex_checksum() {
    assert_eq!(ihex_checksum(&vec![0x10,0x01,0x00,0x00,0x21,0x46,0x01,0x36,0x01,0x21,0x47,0x01,0x36,0x00,0x7E,0xFE,0x09,0xD2,0x19,0x01]), 0x40);
    assert_eq!(ihex_checksum(&vec![0x10,0x01,0x10,0x00,0x21,0x46,0x01,0x7E,0x17,0xC2,0x00,0x01,0xFF,0x5F,0x16,0x00,0x21,0x48,0x01,0x19]), 0x28);
    assert_eq!(ihex_checksum(&vec![0x10,0x01,0x20,0x00,0x19,0x4E,0x79,0x23,0x46,0x23,0x96,0x57,0x78,0x23,0x9E,0xDA,0x3F,0x01,0xB2,0xCA]), 0xA7);
    assert_eq!(ihex_checksum(&vec![0x10,0x01,0x30,0x00,0x3F,0x01,0x56,0x70,0x2B,0x5E,0x71,0x2B,0x72,0x2B,0x73,0x21,0x46,0x01,0x34,0x21]), 0xC7);
    assert_eq!(ihex_checksum(&vec![0x00,0x00,0x00,0x01]), 0xFF);
  }

  #[test]
  fn test_format_record() {
    assert_eq!(format_record(0x0100, &vec![0x21,0x46,0x01,0x36,0x01,0x21,0x47,0x01,0x36,0x00,0x7E,0xFE,0x09,0xD2,0x19,0x01], type_codes::DATA_TYPE_CODE), String::from(":10010000214601360121470136007EFE09D2190140"));
    assert_eq!(format_record(0x0110, &vec![0x21,0x46,0x01,0x7E,0x17,0xC2,0x00,0x01,0xFF,0x5F,0x16,0x00,0x21,0x48,0x01,0x19], type_codes::DATA_TYPE_CODE), String::from(":100110002146017E17C20001FF5F16002148011928"));
    assert_eq!(format_record(0x0120, &vec![0x19,0x4E,0x79,0x23,0x46,0x23,0x96,0x57,0x78,0x23,0x9E,0xDA,0x3F,0x01,0xB2,0xCA], type_codes::DATA_TYPE_CODE), String::from(":10012000194E79234623965778239EDA3F01B2CAA7"));
    assert_eq!(format_record(0x0130, &vec![0x3F,0x01,0x56,0x70,0x2B,0x5E,0x71,0x2B,0x72,0x2B,0x73,0x21,0x46,0x01,0x34,0x21], type_codes::DATA_TYPE_CODE), String::from(":100130003F0156702B5E712B722B732146013421C7"));
    assert_eq!(format_record(0x0000, &[], type_codes::END_OF_FILE_TYPE_CODE), String::from(":00000001FF"));
  }

}
<|endoftext|>