<commit_before>// Take a look at the license at the top of the repository in the LICENSE file.

use crate::ComponentExt;

use std::ptr::null_mut;

use winapi::shared::rpcdce::{
    RPC_C_AUTHN_LEVEL_CALL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
    RPC_C_IMP_LEVEL_IMPERSONATE,
};
use winapi::shared::winerror::{FAILED, SUCCEEDED};
use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER;
use winapi::um::combaseapi::{
    CoCreateInstance, CoInitializeEx, CoInitializeSecurity, CoSetProxyBlanket, CoUninitialize,
};
use winapi::um::oaidl::VARIANT;
use winapi::um::objidl::EOAC_NONE;
use winapi::um::oleauto::{SysAllocString, SysFreeString, VariantClear};
use winapi::um::wbemcli::{
    CLSID_WbemLocator, IEnumWbemClassObject, IID_IWbemLocator, IWbemClassObject, IWbemLocator,
    IWbemServices, WBEM_FLAG_FORWARD_ONLY, WBEM_FLAG_NONSYSTEM_ONLY, WBEM_FLAG_RETURN_IMMEDIATELY,
};

#[doc = include_str!("../../md_doc/component.md")]
pub struct Component {
    temperature: f32,
    max: f32,
    critical: Option<f32>,
    label: String,
    connection: Option<Connection>,
}

impl Component {
    /// Creates a new `Component` with the given information.
    fn new() -> Option<Component> {
        let mut c = Connection::new()
            .and_then(|x| x.initialize_security())
            .and_then(|x| x.create_instance())
            .and_then(|x| x.connect_server())
            .and_then(|x| x.set_proxy_blanket())
            .and_then(|x| x.exec_query())?;

        c.temperature(true)
            .map(|(temperature, critical)| Component {
                temperature,
                label: "Computer".to_owned(),
                max: temperature,
                critical,
                connection: Some(c),
            })
    }
}

impl ComponentExt for Component {
    fn temperature(&self) -> f32 {
        self.temperature
    }

    fn max(&self) -> f32 {
        self.max
    }

    fn critical(&self) -> Option<f32> {
        self.critical
    }

    fn label(&self) -> &str {
        &self.label
    }

    fn refresh(&mut self) {
        if self.connection.is_none() {
            self.connection = Connection::new()
                .and_then(|x| x.initialize_security())
                .and_then(|x| x.create_instance())
                .and_then(|x| x.connect_server())
                .and_then(|x| x.set_proxy_blanket());
        }
        self.connection = if let Some(x) = self.connection.take() {
            x.exec_query()
        } else {
            None
        };
        if let Some(ref mut connection) = self.connection {
            if let Some((temperature, _)) = connection.temperature(false) {
                self.temperature = temperature;
                if self.temperature > self.max {
                    self.max = self.temperature;
                }
            }
        }
    }
}

pub fn get_components() -> Vec<Component> {
    match Component::new() {
        Some(c) => vec![c],
        None => Vec::new(),
    }
}

struct Instance(*mut IWbemLocator);

impl Drop for Instance {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                (*self.0).Release();
            }
        }
    }
}

struct ServerConnection(*mut IWbemServices);

impl Drop for ServerConnection {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                (*self.0).Release();
            }
        }
    }
}

struct Enumerator(*mut IEnumWbemClassObject);

impl Drop for Enumerator {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                (*self.0).Release();
            }
        }
    }
}

macro_rules! bstr {
    ($($x:expr),*) => {{
        let x: &[u16] = &[$($x as u16),*, 0];
        SysAllocString(x.as_ptr())
    }}
}

struct Connection {
    instance: Option<Instance>,
    server_connection: Option<ServerConnection>,
    enumerator: Option<Enumerator>,
}

#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for Connection {}
unsafe impl Sync for Connection {}

impl Connection {
    #[allow(clippy::unnecessary_wraps)]
    fn new() -> Option<Connection> {
        // "Funnily", this function returns ok, false or "this function has already been called".
        // So whatever, let's just ignore whatever it might return then!
        unsafe { CoInitializeEx(null_mut(), 0) };
        Some(Connection {
            instance: None,
            server_connection: None,
            enumerator: None,
        })
    }

    fn initialize_security(self) -> Option<Connection> {
        if FAILED(unsafe {
            CoInitializeSecurity(
                null_mut(),
                -1,
                null_mut(),
                null_mut(),
                RPC_C_AUTHN_LEVEL_DEFAULT,
                RPC_C_IMP_LEVEL_IMPERSONATE,
                null_mut(),
                EOAC_NONE,
                null_mut(),
            )
        }) {
            None
        } else {
            Some(self)
        }
    }

    fn create_instance(mut self) -> Option<Connection> {
        let mut p_loc = null_mut();

        if FAILED(unsafe {
            CoCreateInstance(
                &CLSID_WbemLocator as *const _,
                null_mut(),
                CLSCTX_INPROC_SERVER,
                &IID_IWbemLocator as *const _,
                &mut p_loc as *mut _ as *mut _,
            )
        }) {
            None
        } else {
            self.instance = Some(Instance(p_loc));
            Some(self)
        }
    }

    fn connect_server(mut self) -> Option<Connection> {
        let mut p_svc = null_mut();

        if let Some(ref instance) = self.instance {
            unsafe {
                // "root\WMI"
                let s = bstr!('r', 'o', 'o', 't', '\\', 'W', 'M', 'I');
                let res = (*instance.0).ConnectServer(
                    s,
                    null_mut(),
                    null_mut(),
                    null_mut(),
                    0,
                    null_mut(),
                    null_mut(),
                    &mut p_svc as *mut _,
                );
                SysFreeString(s);
                if FAILED(res) {
                    return None;
                }
            }
        } else {
            return None;
        }
        self.server_connection = Some(ServerConnection(p_svc));
        Some(self)
    }

    fn set_proxy_blanket(self) -> Option<Connection> {
        if let Some(ref server_connection) = self.server_connection {
            unsafe {
                if FAILED(CoSetProxyBlanket(
                    server_connection.0 as *mut _,
                    RPC_C_AUTHN_WINNT,
                    RPC_C_AUTHZ_NONE,
                    null_mut(),
                    RPC_C_AUTHN_LEVEL_CALL,
                    RPC_C_IMP_LEVEL_IMPERSONATE,
                    null_mut(),
                    EOAC_NONE,
                )) {
                    return None;
                }
            }
        } else {
            return None;
        }
        Some(self)
    }

    fn exec_query(mut self) -> Option<Connection> {
        let mut p_enumerator = null_mut();

        if let Some(ref server_connection) = self.server_connection {
            unsafe {
                // "WQL"
                let s = bstr!('W', 'Q', 'L'); // query kind
                                              // "SELECT * FROM MSAcpi_ThermalZoneTemperature"
                let query = bstr!(
                    'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'M', 'S',
                    'A', 'c', 'p', 'i', '_', 'T', 'h', 'e', 'r', 'm', 'a', 'l', 'Z', 'o', 'n', 'e',
                    'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u', 'r', 'e'
                );
                let hres = (*server_connection.0).ExecQuery(
                    s,
                    query,
                    (WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY) as _,
                    null_mut(),
                    &mut p_enumerator as *mut _,
                );
                SysFreeString(s);
                SysFreeString(query);
                if FAILED(hres) {
                    return None;
                }
            }
        } else {
            return None;
        }
        self.enumerator = Some(Enumerator(p_enumerator));
        Some(self)
    }

    fn temperature(&mut self, get_critical: bool) -> Option<(f32, Option<f32>)> {
        let p_enum = match self.enumerator.take() {
            Some(x) => x,
            None => {
                return None;
            }
        };
        let mut p_obj: *mut IWbemClassObject = null_mut();
        let mut nb_returned = 0;

        unsafe {
            use winapi::um::wbemcli::WBEM_INFINITE;
            (*p_enum.0).Next(
                WBEM_INFINITE as _, // Time out
                1,                  // One object
                &mut p_obj as *mut _,
                &mut nb_returned,
            );
        };

        if nb_returned == 0 {
            return None; // not enough rights I suppose...
        }

        unsafe {
            (*p_obj).BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY as _);

            let mut p_val = std::mem::MaybeUninit::<VARIANT>::uninit();
            // "CurrentTemperature"
            let temp = bstr!(
                'C', 'u', 'r', 'r', 'e', 'n', 't', 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u',
                'r', 'e'
            );
            let res = (*p_obj).Get(temp, 0, p_val.as_mut_ptr(), null_mut(), null_mut());
            let mut p_val = p_val.assume_init();

            SysFreeString(temp);
            VariantClear(&mut p_val as *mut _ as *mut _);

            let temp = if SUCCEEDED(res) {
                // temperature is given in tenth of degrees Kelvin
                (p_val.n1.decVal().Lo64 / 10) as f32 - 273.15
            } else {
                (*p_obj).Release();
                return None;
            };

            let mut critical = None;
            if get_critical {
                // "CriticalPoint"
                let crit = bstr!(
                    'C', 'r', 'i', 't', 'i', 'c', 'a', 'l', 'T', 'r', 'i', 'p', 'P', 'o', 'i', 'n',
                    't'
                );
                let res = (*p_obj).Get(crit, 0, &mut p_val, null_mut(), null_mut());

                SysFreeString(crit);
                VariantClear(&mut p_val as *mut _ as *mut _);

                if SUCCEEDED(res) {
                    // temperature is given in tenth of degrees Kelvin
                    critical = Some((p_val.n1.decVal().Lo64 / 10) as f32 - 273.15);
                }
            }
            (*p_obj).Release();
            Some((temp, critical))
        }
    }
}

impl Drop for Connection {
    fn drop(&mut self) {
        // Those three calls are here to enforce that they get dropped in the good order.
        self.enumerator.take();
        self.server_connection.take();
        self.instance.take();
        unsafe {
            CoUninitialize();
        }
    }
}
<commit_msg>check state of COM initialization<commit_after>// Take a look at the license at the top of the repository in the LICENSE file.

use crate::ComponentExt;

use std::ptr::null_mut;

use winapi::shared::rpcdce::{
    RPC_C_AUTHN_LEVEL_CALL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
    RPC_C_IMP_LEVEL_IMPERSONATE,
};
use winapi::shared::winerror::{FAILED, SUCCEEDED, S_FALSE, S_OK};
use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER;
use winapi::um::combaseapi::{
    CoCreateInstance, CoInitializeEx, CoInitializeSecurity, CoSetProxyBlanket, CoUninitialize,
};
use winapi::um::oaidl::VARIANT;
use winapi::um::objidl::EOAC_NONE;
use winapi::um::oleauto::{SysAllocString, SysFreeString, VariantClear};
use winapi::um::wbemcli::{
    CLSID_WbemLocator, IEnumWbemClassObject, IID_IWbemLocator, IWbemClassObject, IWbemLocator,
    IWbemServices, WBEM_FLAG_FORWARD_ONLY, WBEM_FLAG_NONSYSTEM_ONLY, WBEM_FLAG_RETURN_IMMEDIATELY,
};

#[doc = include_str!("../../md_doc/component.md")]
pub struct Component {
    temperature: f32,
    max: f32,
    critical: Option<f32>,
    label: String,
    connection: Option<Connection>,
}

impl Component {
    /// Creates a new `Component` with the given information.
    fn new() -> Option<Component> {
        let mut c = Connection::new()
            .and_then(|x| x.initialize_security())
            .and_then(|x| x.create_instance())
            .and_then(|x| x.connect_server())
            .and_then(|x| x.set_proxy_blanket())
            .and_then(|x| x.exec_query())?;

        c.temperature(true)
            .map(|(temperature, critical)| Component {
                temperature,
                label: "Computer".to_owned(),
                max: temperature,
                critical,
                connection: Some(c),
            })
    }
}

impl ComponentExt for Component {
    fn temperature(&self) -> f32 {
        self.temperature
    }

    fn max(&self) -> f32 {
        self.max
    }

    fn critical(&self) -> Option<f32> {
        self.critical
    }

    fn label(&self) -> &str {
        &self.label
    }

    fn refresh(&mut self) {
        if self.connection.is_none() {
            self.connection = Connection::new()
                .and_then(|x| x.initialize_security())
                .and_then(|x| x.create_instance())
                .and_then(|x| x.connect_server())
                .and_then(|x| x.set_proxy_blanket());
        }
        self.connection = if let Some(x) = self.connection.take() {
            x.exec_query()
        } else {
            None
        };
        if let Some(ref mut connection) = self.connection {
            if let Some((temperature, _)) = connection.temperature(false) {
                self.temperature = temperature;
                if self.temperature > self.max {
                    self.max = self.temperature;
                }
            }
        }
    }
}

pub fn get_components() -> Vec<Component> {
    match Component::new() {
        Some(c) => vec![c],
        None => Vec::new(),
    }
}

struct Instance(*mut IWbemLocator);

impl Drop for Instance {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                (*self.0).Release();
            }
        }
    }
}

struct ServerConnection(*mut IWbemServices);

impl Drop for ServerConnection {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                (*self.0).Release();
            }
        }
    }
}

struct Enumerator(*mut IEnumWbemClassObject);

impl Drop for Enumerator {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                (*self.0).Release();
            }
        }
    }
}

macro_rules! bstr {
    ($($x:expr),*) => {{
        let x: &[u16] = &[$($x as u16),*, 0];
        SysAllocString(x.as_ptr())
    }}
}

struct Connection {
    instance: Option<Instance>,
    server_connection: Option<ServerConnection>,
    enumerator: Option<Enumerator>,
    initialized: bool,
}

#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for Connection {}
unsafe impl Sync for Connection {}

impl Connection {
    #[allow(clippy::unnecessary_wraps)]
    fn new() -> Option<Connection> {
        let val = unsafe { CoInitializeEx(null_mut(), 0) };
        Some(Connection {
            instance: None,
            server_connection: None,
            enumerator: None,
            initialized: val == S_OK || val == S_FALSE,
        })
    }

    fn initialize_security(self) -> Option<Connection> {
        if FAILED(unsafe {
            CoInitializeSecurity(
                null_mut(),
                -1,
                null_mut(),
                null_mut(),
                RPC_C_AUTHN_LEVEL_DEFAULT,
                RPC_C_IMP_LEVEL_IMPERSONATE,
                null_mut(),
                EOAC_NONE,
                null_mut(),
            )
        }) {
            None
        } else {
            Some(self)
        }
    }

    fn create_instance(mut self) -> Option<Connection> {
        let mut p_loc = null_mut();

        if FAILED(unsafe {
            CoCreateInstance(
                &CLSID_WbemLocator as *const _,
                null_mut(),
                CLSCTX_INPROC_SERVER,
                &IID_IWbemLocator as *const _,
                &mut p_loc as *mut _ as *mut _,
            )
        }) {
            None
        } else {
            self.instance = Some(Instance(p_loc));
            Some(self)
        }
    }

    fn connect_server(mut self) -> Option<Connection> {
        let mut p_svc = null_mut();

        if let Some(ref instance) = self.instance {
            unsafe {
                // "root\WMI"
                let s = bstr!('r', 'o', 'o', 't', '\\', 'W', 'M', 'I');
                let res = (*instance.0).ConnectServer(
                    s,
                    null_mut(),
                    null_mut(),
                    null_mut(),
                    0,
                    null_mut(),
                    null_mut(),
                    &mut p_svc as *mut _,
                );
                SysFreeString(s);
                if FAILED(res) {
                    return None;
                }
            }
        } else {
            return None;
        }
        self.server_connection = Some(ServerConnection(p_svc));
        Some(self)
    }

    fn set_proxy_blanket(self) -> Option<Connection> {
        if let Some(ref server_connection) = self.server_connection {
            unsafe {
                if FAILED(CoSetProxyBlanket(
                    server_connection.0 as *mut _,
                    RPC_C_AUTHN_WINNT,
                    RPC_C_AUTHZ_NONE,
                    null_mut(),
                    RPC_C_AUTHN_LEVEL_CALL,
                    RPC_C_IMP_LEVEL_IMPERSONATE,
                    null_mut(),
                    EOAC_NONE,
                )) {
                    return None;
                }
            }
        } else {
            return None;
        }
        Some(self)
    }

    fn exec_query(mut self) -> Option<Connection> {
        let mut p_enumerator = null_mut();

        if let Some(ref server_connection) = self.server_connection {
            unsafe {
                // "WQL"
                let s = bstr!('W', 'Q', 'L'); // query kind
                                              // "SELECT * FROM MSAcpi_ThermalZoneTemperature"
                let query = bstr!(
                    'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'M', 'S',
                    'A', 'c', 'p', 'i', '_', 'T', 'h', 'e', 'r', 'm', 'a', 'l', 'Z', 'o', 'n', 'e',
                    'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u', 'r', 'e'
                );
                let hres = (*server_connection.0).ExecQuery(
                    s,
                    query,
                    (WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY) as _,
                    null_mut(),
                    &mut p_enumerator as *mut _,
                );
                SysFreeString(s);
                SysFreeString(query);
                if FAILED(hres) {
                    return None;
                }
            }
        } else {
            return None;
        }
        self.enumerator = Some(Enumerator(p_enumerator));
        Some(self)
    }

    fn temperature(&mut self, get_critical: bool) -> Option<(f32, Option<f32>)> {
        let p_enum = match self.enumerator.take() {
            Some(x) => x,
            None => {
                return None;
            }
        };
        let mut p_obj: *mut IWbemClassObject = null_mut();
        let mut nb_returned = 0;

        unsafe {
            use winapi::um::wbemcli::WBEM_INFINITE;
            (*p_enum.0).Next(
                WBEM_INFINITE as _, // Time out
                1,                  // One object
                &mut p_obj as *mut _,
                &mut nb_returned,
            );
        };

        if nb_returned == 0 {
            return None; // not enough rights I suppose...
        }

        unsafe {
            (*p_obj).BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY as _);

            let mut p_val = std::mem::MaybeUninit::<VARIANT>::uninit();
            // "CurrentTemperature"
            let temp = bstr!(
                'C', 'u', 'r', 'r', 'e', 'n', 't', 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u',
                'r', 'e'
            );
            let res = (*p_obj).Get(temp, 0, p_val.as_mut_ptr(), null_mut(), null_mut());
            let mut p_val = p_val.assume_init();

            SysFreeString(temp);
            VariantClear(&mut p_val as *mut _ as *mut _);

            let temp = if SUCCEEDED(res) {
                // temperature is given in tenth of degrees Kelvin
                (p_val.n1.decVal().Lo64 / 10) as f32 - 273.15
            } else {
                (*p_obj).Release();
                return None;
            };

            let mut critical = None;
            if get_critical {
                // "CriticalPoint"
                let crit = bstr!(
                    'C', 'r', 'i', 't', 'i', 'c', 'a', 'l', 'T', 'r', 'i', 'p', 'P', 'o', 'i', 'n',
                    't'
                );
                let res = (*p_obj).Get(crit, 0, &mut p_val, null_mut(), null_mut());

                SysFreeString(crit);
                VariantClear(&mut p_val as *mut _ as *mut _);

                if SUCCEEDED(res) {
                    // temperature is given in tenth of degrees Kelvin
                    critical = Some((p_val.n1.decVal().Lo64 / 10) as f32 - 273.15);
                }
            }
            (*p_obj).Release();
            Some((temp, critical))
        }
    }
}

impl Drop for Connection {
    fn drop(&mut self) {
        // Those three calls are here to enforce that they get dropped in the good order.
        self.enumerator.take();
        self.server_connection.take();
        self.instance.take();
        if self.initialized {
            unsafe {
                CoUninitialize();
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ops::Add;
use libnum::{self, Zero, Float};

use imp_prelude::*;
use numeric_util;

use {
    LinalgScalar,
    aview0,
};

impl<A, S, D> ArrayBase<S, D>
    where S: Data<Elem=A>,
          D: Dimension,
{
    /// Return sum along `axis`.
    ///
    /// ```
    /// use ndarray::{aview0, aview1, arr2, Axis};
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert!(
    ///     a.sum(Axis(0)) == aview1(&[4., 6.]) &&
    ///     a.sum(Axis(1)) == aview1(&[3., 7.]) &&
    ///
    ///     a.sum(Axis(0)).sum(Axis(0)) == aview0(&10.)
    /// );
    /// ```
    ///
    /// **Panics** if `axis` is out of bounds.
    pub fn sum(&self, axis: Axis) -> OwnedArray<A, <D as RemoveAxis>::Smaller>
        where A: Clone + Zero + Add<Output=A>,
              D: RemoveAxis,
    {
        let n = self.shape().axis(axis);
        let mut res = self.subview(axis, 0).to_owned();
        for i in 1..n {
            let view = self.subview(axis, i);
            res = res + &view;
        }
        res
    }

    /// Return the sum of all elements in the array.
    ///
    /// ```
    /// use ndarray::arr2;
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert_eq!(a.scalar_sum(), 10.);
    /// ```
    pub fn scalar_sum(&self) -> A
        where A: Clone + Add<Output=A> + libnum::Zero,
    {
        if let Some(slc) = self.as_slice_memory_order() {
            return numeric_util::unrolled_sum(slc);
        }
        let mut sum = A::zero();
        for row in self.inner_iter() {
            if let Some(slc) = row.as_slice() {
                sum = sum + numeric_util::unrolled_sum(slc);
            } else {
                sum = sum + row.iter().fold(A::zero(), |acc, elt| acc + elt.clone());
            }
        }
        sum
    }

    /// Return mean along `axis`.
    ///
    /// **Panics** if `axis` is out of bounds.
    ///
    /// ```
    /// use ndarray::{aview1, arr2, Axis};
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert!(
    ///     a.mean(Axis(0)) == aview1(&[2.0, 3.0]) &&
    ///     a.mean(Axis(1)) == aview1(&[1.5, 3.5])
    /// );
    /// ```
    pub fn mean(&self, axis: Axis) -> OwnedArray<A, <D as RemoveAxis>::Smaller>
        where A: LinalgScalar,
              D: RemoveAxis,
    {
        let n = self.shape().axis(axis);
        let sum = self.sum(axis);
        let mut cnt = A::one();
        for _ in 1..n {
            cnt = cnt + A::one();
        }
        sum / &aview0(&cnt)
    }

    /// Return `true` if the arrays' elementwise differences are all within
    /// the given absolute tolerance, `false` otherwise.
    ///
    /// If their shapes disagree, `rhs` is broadcast to the shape of `self`.
    ///
    /// **Panics** if broadcasting to the same shape isn’t possible.
    pub fn all_close<S2, E>(&self, rhs: &ArrayBase<S2, E>, tol: A) -> bool
        where A: Float,
              S2: Data<Elem=A>,
              E: Dimension,
    {
        let rhs_broadcast = rhs.broadcast_unwrap(self.dim());
        self.iter().zip(rhs_broadcast.iter()).all(|(x, y)| (*x - *y).abs() <= tol)
    }
}

<commit_msg>Move scalar_sum to be the first numeric method<commit_after>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ops::Add;
use libnum::{self, Zero, Float};

use imp_prelude::*;
use numeric_util;

use {
    LinalgScalar,
    aview0,
};

/// Numerical methods for arrays.
impl<A, S, D> ArrayBase<S, D>
    where S: Data<Elem=A>,
          D: Dimension,
{
    /// Return the sum of all elements in the array.
    ///
    /// ```
    /// use ndarray::arr2;
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert_eq!(a.scalar_sum(), 10.);
    /// ```
    pub fn scalar_sum(&self) -> A
        where A: Clone + Add<Output=A> + libnum::Zero,
    {
        if let Some(slc) = self.as_slice_memory_order() {
            return numeric_util::unrolled_sum(slc);
        }
        let mut sum = A::zero();
        for row in self.inner_iter() {
            if let Some(slc) = row.as_slice() {
                sum = sum + numeric_util::unrolled_sum(slc);
            } else {
                sum = sum + row.iter().fold(A::zero(), |acc, elt| acc + elt.clone());
            }
        }
        sum
    }

    /// Return sum along `axis`.
    ///
    /// ```
    /// use ndarray::{aview0, aview1, arr2, Axis};
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert!(
    ///     a.sum(Axis(0)) == aview1(&[4., 6.]) &&
    ///     a.sum(Axis(1)) == aview1(&[3., 7.]) &&
    ///
    ///     a.sum(Axis(0)).sum(Axis(0)) == aview0(&10.)
    /// );
    /// ```
    ///
    /// **Panics** if `axis` is out of bounds.
    pub fn sum(&self, axis: Axis) -> OwnedArray<A, <D as RemoveAxis>::Smaller>
        where A: Clone + Zero + Add<Output=A>,
              D: RemoveAxis,
    {
        let n = self.shape().axis(axis);
        let mut res = self.subview(axis, 0).to_owned();
        for i in 1..n {
            let view = self.subview(axis, i);
            res = res + &view;
        }
        res
    }

    /// Return mean along `axis`.
    ///
    /// **Panics** if `axis` is out of bounds.
    ///
    /// ```
    /// use ndarray::{aview1, arr2, Axis};
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert!(
    ///     a.mean(Axis(0)) == aview1(&[2.0, 3.0]) &&
    ///     a.mean(Axis(1)) == aview1(&[1.5, 3.5])
    /// );
    /// ```
    pub fn mean(&self, axis: Axis) -> OwnedArray<A, <D as RemoveAxis>::Smaller>
        where A: LinalgScalar,
              D: RemoveAxis,
    {
        let n = self.shape().axis(axis);
        let sum = self.sum(axis);
        let mut cnt = A::one();
        for _ in 1..n {
            cnt = cnt + A::one();
        }
        sum / &aview0(&cnt)
    }

    /// Return `true` if the arrays' elementwise differences are all within
    /// the given absolute tolerance, `false` otherwise.
    ///
    /// If their shapes disagree, `rhs` is broadcast to the shape of `self`.
    ///
    /// **Panics** if broadcasting to the same shape isn’t possible.
    pub fn all_close<S2, E>(&self, rhs: &ArrayBase<S2, E>, tol: A) -> bool
        where A: Float,
              S2: Data<Elem=A>,
              E: Dimension,
    {
        let rhs_broadcast = rhs.broadcast_unwrap(self.dim());
        self.iter().zip(rhs_broadcast.iter()).all(|(x, y)| (*x - *y).abs() <= tol)
    }
}

<|endoftext|>


<commit_before>use audio::sample::Sample;
use audio::{cpal, Device};
use std;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::atomic::{self, AtomicBool};
use std::sync::{mpsc, Arc, Mutex};

/// Items related to output audio streams.
pub mod output;

/// Items related to input audio streams.
pub mod input;

/// Items related to duplex (synchronised input/output) audio streams.
///
/// *Progress is currently pending implementation of input audio streams in CPAL.*
pub mod duplex {}

/// The default sample rate used for output, input and duplex streams if possible.
pub const DEFAULT_SAMPLE_RATE: u32 = 44_100;

/// The type of function accepted for model updates.
pub type UpdateFn<M> = FnOnce(&mut M) + Send + 'static;

pub(crate) type ProcessFn = FnMut(cpal::StreamData) + 'static + Send;
pub(crate) type ProcessFnMsg = (cpal::StreamId, Box<ProcessFn>);

// State that is updated and run on the `cpal::EventLoop::run` thread.
pub(crate) struct LoopContext {
    // A channel for receiving callback functions for newly spawned streams.
    process_fn_rx: mpsc::Receiver<ProcessFnMsg>,
    // A map from StreamIds to their associated buffer processing functions.
    process_fns: HashMap<cpal::StreamId, Box<ProcessFn>>,
}

/// A clone-able handle around an audio stream.
pub struct Stream<M> {
    /// A channel for sending model updates to the audio thread.
    update_tx: mpsc::Sender<Box<FnMut(&mut M) + 'static + Send>>,
    /// A channel used for sending through a new buffer processing callback to the event loop.
    process_fn_tx: mpsc::Sender<ProcessFnMsg>,
    /// Data shared between each `Stream` handle to a single stream.
    shared: Arc<Shared<M>>,
    /// The format with which the stream was created.
    cpal_format: cpal::Format,
}

// Data shared between each `Stream` handle to a single stream.
struct Shared<M> {
    // The user's audio model
    model: Arc<Mutex<Option<M>>>,
    // A unique ID associated with this stream on the cpal EventLoop.
    stream_id: cpal::StreamId,
    // A handle to the CPAL audio event loop.
    event_loop: Arc<cpal::EventLoop>,
    // Whether or not the stream is currently paused.
    is_paused: AtomicBool,
}

/// Stream building parameters that are common between input and output streams.
pub struct Builder<M, S = f32> {
    pub(crate) event_loop: Arc<cpal::EventLoop>,
    pub(crate) process_fn_tx: mpsc::Sender<ProcessFnMsg>,
    pub model: M,
    pub sample_rate: Option<u32>,
    pub channels: Option<usize>,
    pub frames_per_buffer: Option<usize>,
    pub device: Option<Device>,
    pub(crate) sample_format: PhantomData<S>,
}

#[derive(Debug)]
pub enum BuildError {
    DefaultDevice,
    FormatEnumeration(cpal::FormatsEnumerationError),
    Creation(cpal::CreationError),
}

impl LoopContext {
    /// Create a new loop context.
    pub fn new(process_fn_rx: mpsc::Receiver<ProcessFnMsg>) -> Self {
        let process_fns = HashMap::new();
        LoopContext {
            process_fn_rx,
            process_fns,
        }
    }

    /// Process the given buffer with the stream at the given ID.
    pub fn process(&mut self, stream_id: cpal::StreamId, data: cpal::StreamData) {
        // Collect any pending stream process fns.
        for (stream_id, proc_fn) in self.process_fn_rx.try_iter() {
            self.process_fns.insert(stream_id, proc_fn);
        }

        // Process the data using the stream at the given ID.
        if let Some(proc_fn) = self.process_fns.get_mut(&stream_id) {
            proc_fn(data);
        // If there is not yet a buffer processing function, just silence the output buffer.
        } else {
            if let cpal::StreamData::Output { mut buffer } = data {
                fn silence<S: Sample>(slice: &mut [S]) {
                    for sample in slice {
                        *sample = S::equilibrium();
                    }
                }
                match buffer {
                    cpal::UnknownTypeOutputBuffer::U16(ref mut buffer) => silence(buffer),
                    cpal::UnknownTypeOutputBuffer::I16(ref mut buffer) => silence(buffer),
                    cpal::UnknownTypeOutputBuffer::F32(ref mut buffer) => silence(buffer),
                }
            }
        }
    }
}

impl<M> Stream<M> {
    /// Command the audio device to start processing this stream.
    ///
    /// Calling this will activate capturing/rendering, in turn calling the given audio
    /// capture/render function.
    ///
    /// Has no effect if the stream is already running.
    pub fn play(&self) {
        self.shared.play()
    }

    /// Command the audio device to stop processing this stream.
    ///
    /// Calling this will pause rendering/capturing.
    ///
    /// Has no effect is the stream was already paused.
    pub fn pause(&self) {
        self.shared.pause()
    }

    /// Whether or not the stream is currently playing.
    pub fn is_playing(&self) -> bool {
        self.shared.is_playing()
    }

    /// Whether or not the stream is currently paused.
    pub fn is_paused(&self) -> bool {
        self.shared.is_paused()
    }

    /// Send the given model update to the audio thread to be applied ASAP.
    ///
    /// If the audio is currently rendering, the update will be applied immediately after the
    /// function call completes.
    ///
    /// If the stream is currently paused, the update will be applied immediately.
    ///
    /// **Note:** This function will be applied on the real-time audio thread so users should
    /// avoid performing any kind of I/O, locking, blocking, (de)allocations or anything that
    /// may run for an indeterminate amount of time.
    pub fn send<F>(
        &self,
        update: F,
    ) -> Result<(), mpsc::SendError<Box<FnMut(&mut M) + Send + 'static>>>
    where
        F: FnOnce(&mut M) + Send + 'static,
    {
        // NOTE: The following code may mean that on extremely rare occasions an update does
        // not get applied for an indeterminate amount of time. This might be the case if a
        // stream is unpaused but becomes paused *immediately* after the `is_paused` atomic
        // condition is read as `false` - the update would be sent but the stream would be
        // paused and in turn the update will not get processed until the stream is unpaused
        // again. It would be nice to work out a solution to this that does not require
        // spawning another thread for each stream.

        // If the thread is currently paused, take the lock and immediately apply it as we know
        // there will be no contention with the audio thread.
        if self.shared.is_paused.load(atomic::Ordering::Relaxed) {
            if let Ok(mut guard) = self.shared.model.lock() {
                let mut model = guard.take().unwrap();
                update(&mut model);
                *guard = Some(model);
            }
        // Otherwise send the update to the audio thread.
        } else {
            // Move the `FnOnce` into a `FnMut` closure so that it can be called when it gets to
            // the audio thread. We do this as it's currently not possible to call a `Box<FnOnce>`,
            // as `FnOnce`'s `call` method takes `self` by value and thus is technically not object
            // safe.
            let mut update_opt = Some(update);
            let update_fn = move |audio: &mut M| {
                if let Some(update) = update_opt.take() {
                    update(audio);
                }
            };
            self.update_tx.send(Box::new(update_fn))?;
        }

        Ok(())
    }

    /// The format with which the inner CPAL stream was created.
    ///
    /// This **should** match the actual stream format that is running. If not, there may be a bug
    /// in CPAL. However, note that if the `sample_format` does not match, this just means that
    /// `nannou` is doing a conversion behind the scenes as the hardware itself does not support
    /// the target format.
    pub fn cpal_format(&self) -> &cpal::Format {
        &self.cpal_format
    }

    /// A reference to the unique ID associated with this stream.
    pub fn id(&self) -> &cpal::StreamId {
        &self.shared.stream_id
    }
}

impl<M> Shared<M> {
    fn play(&self) {
        self.event_loop.play_stream(self.stream_id.clone());
        self.is_paused.store(false, atomic::Ordering::Relaxed);
    }

    fn pause(&self) {
        self.is_paused.store(true, atomic::Ordering::Relaxed);
        self.event_loop.pause_stream(self.stream_id.clone());
    }

    fn is_playing(&self) -> bool {
        !self.is_paused.load(atomic::Ordering::Relaxed)
    }

    fn is_paused(&self) -> bool {
        self.is_paused.load(atomic::Ordering::Relaxed)
    }
}

impl<M> Clone for Stream<M> {
    fn clone(&self) -> Self {
        let update_tx = self.update_tx.clone();
        let process_fn_tx = self.process_fn_tx.clone();
        let shared = self.shared.clone();
        let cpal_format = self.cpal_format.clone();
        Stream {
            update_tx,
            process_fn_tx,
            shared,
            cpal_format,
        }
    }
}

impl<M> Drop for Shared<M> {
    fn drop(&mut self) {
        if self.is_playing() {
            self.pause();
        }
        self.event_loop.destroy_stream(self.stream_id.clone());
    }
}

impl From<cpal::CreationError> for BuildError {
    fn from(e: cpal::CreationError) -> Self {
        BuildError::Creation(e)
    }
}

impl From<cpal::FormatsEnumerationError> for BuildError {
    fn from(e: cpal::FormatsEnumerationError) -> Self {
        BuildError::FormatEnumeration(e)
    }
}

impl std::error::Error for BuildError {
    fn description(&self) -> &str {
        match *self {
            BuildError::DefaultDevice => "Failed to get default device",
            BuildError::FormatEnumeration(ref err) => err.description(),
            BuildError::Creation(ref err) => err.description(),
        }
    }
}

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

// Checks if the target format has a CPAL sample format equivalent.
//
// If so, we can use the sample format to target a stream format that already has a
// matchingn sample format.
//
// Otherwise we'll just fall back to the default sample format and do a conversionn.
fn cpal_sample_format<S: Any>() -> Option<cpal::SampleFormat> {
    let type_id = TypeId::of::<S>();
    if type_id == TypeId::of::<f32>() {
        Some(cpal::SampleFormat::F32)
    } else if type_id == TypeId::of::<i16>() {
        Some(cpal::SampleFormat::I16)
    } else if type_id == TypeId::of::<u16>() {
        Some(cpal::SampleFormat::U16)
    } else {
        None
    }
}

// Nannou allows the user to optionally specify each part of the stream format.
//
// This function is used to determine whether or not a supported format yielded by CPAL matches
// the requested format parameters specified by the user. If the supported format matches, a
// compatible `Format` is returned.
fn matching_supported_formats(
    mut supported_format: cpal::SupportedFormat,
    sample_format: Option<cpal::SampleFormat>,
    channels: Option<usize>,
    sample_rate: Option<cpal::SampleRate>,
) -> Option<cpal::Format> {
    // Check for a matching sample format.
    if let Some(sample_format) = sample_format {
        if supported_format.data_type != sample_format {
            return None;
        }
    }
    // Check for a matching number of channels.
    //
    // If there are more than enough channels, truncate the `SupportedFormat` to match.
    if let Some(channels) = channels {
        let supported_channels = supported_format.channels as usize;
        if supported_channels < channels {
            return None;
        } else if supported_channels > channels {
            supported_format.channels = channels as u16;
        }
    }
    // Check the sample rate.
    if let Some(sample_rate) = sample_rate {
        if supported_format.min_sample_rate > sample_rate
            || supported_format.max_sample_rate < sample_rate
        {
            return None;
        }
        let mut format = supported_format.with_max_sample_rate();
        format.sample_rate = sample_rate;
        return Some(format);
    }
    Some(supported_format.with_max_sample_rate())
}

// Given some audio device find the supported stream format that best matches the given optional
// format parameters (specified by the user).
fn find_best_matching_format<F>(
    device: &cpal::Device,
    mut sample_format: Option<cpal::SampleFormat>,
    channels: Option<usize>,
    mut sample_rate: Option<cpal::SampleRate>,
    default_format: Option<cpal::Format>,
    supported_formats: F,
) -> Result<Option<cpal::Format>, cpal::FormatsEnumerationError>
where
    F: Fn(&cpal::Device) -> Result<Vec<cpal::SupportedFormat>, cpal::FormatsEnumerationError>,
{
    loop {
        {
            // First, see if the default format satisfies the request.
            if let Some(ref format) = default_format {
                if sample_format == Some(format.data_type)
                    && channels
                        .map(|ch| ch <= format.channels as usize)
                        .unwrap_or(true)
                    && sample_rate == Some(format.sample_rate)
                {
                    return Ok(Some(format.clone()));
                }
            }

            // Otherwise search through all supported formats for compatible formats.
            let mut stream_formats = supported_formats(device)?.into_iter().filter_map(|fmt| {
                matching_supported_formats(fmt, sample_format, channels, sample_rate)
            });

            // Find the supported format with the most channels (this will always be the target
            // number of channels if some specific target number was specified as all other numbers
            // will have been filtered out already).
            if let Some(first) = stream_formats.next() {
                let format = stream_formats.fold(first, |max, fmt| {
                    if fmt.channels > max.channels {
                        fmt
                    } else {
                        max
                    }
                });
                return Ok(Some(format));
            }
        }

        // If there are no matching formats with the target sample_format, drop the requirement
        // and we'll do a conversion from the default format instead.
        if sample_format.is_some() {
            sample_format = default_format.as_ref().map(|fmt| fmt.data_type);
        // Otherwise if nannou's default target sample rate is set because the user didn't
        // specify a sample rate, try and fall back to a supported sample rate in case this was
        // the reason we could not find a supported format.
        } else if sample_rate == Some(cpal::SampleRate(DEFAULT_SAMPLE_RATE)) {
            let cpal_default_sample_rate = default_format.as_ref().map(|fmt| fmt.sample_rate);
            let nannou_default_sample_rate = Some(cpal::SampleRate(DEFAULT_SAMPLE_RATE));
            sample_rate = if cpal_default_sample_rate != nannou_default_sample_rate {
                cpal_default_sample_rate
            } else {
                None
            };
        } else {
            return Ok(None);
        }
    }
}
<commit_msg>Avoid infinite loop when finding best sample format for audio stream<commit_after>use audio::sample::Sample;
use audio::{cpal, Device};
use std;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::atomic::{self, AtomicBool};
use std::sync::{mpsc, Arc, Mutex};

/// Items related to output audio streams.
pub mod output;

/// Items related to input audio streams.
pub mod input;

/// Items related to duplex (synchronised input/output) audio streams.
///
/// *Progress is currently pending implementation of input audio streams in CPAL.*
pub mod duplex {}

/// The default sample rate used for output, input and duplex streams if possible.
pub const DEFAULT_SAMPLE_RATE: u32 = 44_100;

/// The type of function accepted for model updates.
pub type UpdateFn<M> = FnOnce(&mut M) + Send + 'static;

pub(crate) type ProcessFn = FnMut(cpal::StreamData) + 'static + Send;
pub(crate) type ProcessFnMsg = (cpal::StreamId, Box<ProcessFn>);

// State that is updated and run on the `cpal::EventLoop::run` thread.
pub(crate) struct LoopContext {
    // A channel for receiving callback functions for newly spawned streams.
    process_fn_rx: mpsc::Receiver<ProcessFnMsg>,
    // A map from StreamIds to their associated buffer processing functions.
    process_fns: HashMap<cpal::StreamId, Box<ProcessFn>>,
}

/// A clone-able handle around an audio stream.
pub struct Stream<M> {
    /// A channel for sending model updates to the audio thread.
    update_tx: mpsc::Sender<Box<FnMut(&mut M) + 'static + Send>>,
    /// A channel used for sending through a new buffer processing callback to the event loop.
    process_fn_tx: mpsc::Sender<ProcessFnMsg>,
    /// Data shared between each `Stream` handle to a single stream.
    shared: Arc<Shared<M>>,
    /// The format with which the stream was created.
    cpal_format: cpal::Format,
}

// Data shared between each `Stream` handle to a single stream.
struct Shared<M> {
    // The user's audio model
    model: Arc<Mutex<Option<M>>>,
    // A unique ID associated with this stream on the cpal EventLoop.
    stream_id: cpal::StreamId,
    // A handle to the CPAL audio event loop.
    event_loop: Arc<cpal::EventLoop>,
    // Whether or not the stream is currently paused.
    is_paused: AtomicBool,
}

/// Stream building parameters that are common between input and output streams.
pub struct Builder<M, S = f32> {
    pub(crate) event_loop: Arc<cpal::EventLoop>,
    pub(crate) process_fn_tx: mpsc::Sender<ProcessFnMsg>,
    pub model: M,
    pub sample_rate: Option<u32>,
    pub channels: Option<usize>,
    pub frames_per_buffer: Option<usize>,
    pub device: Option<Device>,
    pub(crate) sample_format: PhantomData<S>,
}

#[derive(Debug)]
pub enum BuildError {
    DefaultDevice,
    FormatEnumeration(cpal::FormatsEnumerationError),
    Creation(cpal::CreationError),
}

impl LoopContext {
    /// Create a new loop context.
    pub fn new(process_fn_rx: mpsc::Receiver<ProcessFnMsg>) -> Self {
        let process_fns = HashMap::new();
        LoopContext {
            process_fn_rx,
            process_fns,
        }
    }

    /// Process the given buffer with the stream at the given ID.
    pub fn process(&mut self, stream_id: cpal::StreamId, data: cpal::StreamData) {
        // Collect any pending stream process fns.
        for (stream_id, proc_fn) in self.process_fn_rx.try_iter() {
            self.process_fns.insert(stream_id, proc_fn);
        }

        // Process the data using the stream at the given ID.
        if let Some(proc_fn) = self.process_fns.get_mut(&stream_id) {
            proc_fn(data);
        // If there is not yet a buffer processing function, just silence the output buffer.
        } else {
            if let cpal::StreamData::Output { mut buffer } = data {
                fn silence<S: Sample>(slice: &mut [S]) {
                    for sample in slice {
                        *sample = S::equilibrium();
                    }
                }
                match buffer {
                    cpal::UnknownTypeOutputBuffer::U16(ref mut buffer) => silence(buffer),
                    cpal::UnknownTypeOutputBuffer::I16(ref mut buffer) => silence(buffer),
                    cpal::UnknownTypeOutputBuffer::F32(ref mut buffer) => silence(buffer),
                }
            }
        }
    }
}

impl<M> Stream<M> {
    /// Command the audio device to start processing this stream.
    ///
    /// Calling this will activate capturing/rendering, in turn calling the given audio
    /// capture/render function.
    ///
    /// Has no effect if the stream is already running.
    pub fn play(&self) {
        self.shared.play()
    }

    /// Command the audio device to stop processing this stream.
    ///
    /// Calling this will pause rendering/capturing.
    ///
    /// Has no effect is the stream was already paused.
    pub fn pause(&self) {
        self.shared.pause()
    }

    /// Whether or not the stream is currently playing.
    pub fn is_playing(&self) -> bool {
        self.shared.is_playing()
    }

    /// Whether or not the stream is currently paused.
    pub fn is_paused(&self) -> bool {
        self.shared.is_paused()
    }

    /// Send the given model update to the audio thread to be applied ASAP.
    ///
    /// If the audio is currently rendering, the update will be applied immediately after the
    /// function call completes.
    ///
    /// If the stream is currently paused, the update will be applied immediately.
    ///
    /// **Note:** This function will be applied on the real-time audio thread so users should
    /// avoid performing any kind of I/O, locking, blocking, (de)allocations or anything that
    /// may run for an indeterminate amount of time.
    pub fn send<F>(
        &self,
        update: F,
    ) -> Result<(), mpsc::SendError<Box<FnMut(&mut M) + Send + 'static>>>
    where
        F: FnOnce(&mut M) + Send + 'static,
    {
        // NOTE: The following code may mean that on extremely rare occasions an update does
        // not get applied for an indeterminate amount of time. This might be the case if a
        // stream is unpaused but becomes paused *immediately* after the `is_paused` atomic
        // condition is read as `false` - the update would be sent but the stream would be
        // paused and in turn the update will not get processed until the stream is unpaused
        // again. It would be nice to work out a solution to this that does not require
        // spawning another thread for each stream.

        // If the thread is currently paused, take the lock and immediately apply it as we know
        // there will be no contention with the audio thread.
        if self.shared.is_paused.load(atomic::Ordering::Relaxed) {
            if let Ok(mut guard) = self.shared.model.lock() {
                let mut model = guard.take().unwrap();
                update(&mut model);
                *guard = Some(model);
            }
        // Otherwise send the update to the audio thread.
        } else {
            // Move the `FnOnce` into a `FnMut` closure so that it can be called when it gets to
            // the audio thread. We do this as it's currently not possible to call a `Box<FnOnce>`,
            // as `FnOnce`'s `call` method takes `self` by value and thus is technically not object
            // safe.
            let mut update_opt = Some(update);
            let update_fn = move |audio: &mut M| {
                if let Some(update) = update_opt.take() {
                    update(audio);
                }
            };
            self.update_tx.send(Box::new(update_fn))?;
        }

        Ok(())
    }

    /// The format with which the inner CPAL stream was created.
    ///
    /// This **should** match the actual stream format that is running. If not, there may be a bug
    /// in CPAL. However, note that if the `sample_format` does not match, this just means that
    /// `nannou` is doing a conversion behind the scenes as the hardware itself does not support
    /// the target format.
    pub fn cpal_format(&self) -> &cpal::Format {
        &self.cpal_format
    }

    /// A reference to the unique ID associated with this stream.
    pub fn id(&self) -> &cpal::StreamId {
        &self.shared.stream_id
    }
}

impl<M> Shared<M> {
    fn play(&self) {
        self.event_loop.play_stream(self.stream_id.clone());
        self.is_paused.store(false, atomic::Ordering::Relaxed);
    }

    fn pause(&self) {
        self.is_paused.store(true, atomic::Ordering::Relaxed);
        self.event_loop.pause_stream(self.stream_id.clone());
    }

    fn is_playing(&self) -> bool {
        !self.is_paused.load(atomic::Ordering::Relaxed)
    }

    fn is_paused(&self) -> bool {
        self.is_paused.load(atomic::Ordering::Relaxed)
    }
}

impl<M> Clone for Stream<M> {
    fn clone(&self) -> Self {
        let update_tx = self.update_tx.clone();
        let process_fn_tx = self.process_fn_tx.clone();
        let shared = self.shared.clone();
        let cpal_format = self.cpal_format.clone();
        Stream {
            update_tx,
            process_fn_tx,
            shared,
            cpal_format,
        }
    }
}

impl<M> Drop for Shared<M> {
    fn drop(&mut self) {
        if self.is_playing() {
            self.pause();
        }
        self.event_loop.destroy_stream(self.stream_id.clone());
    }
}

impl From<cpal::CreationError> for BuildError {
    fn from(e: cpal::CreationError) -> Self {
        BuildError::Creation(e)
    }
}

impl From<cpal::FormatsEnumerationError> for BuildError {
    fn from(e: cpal::FormatsEnumerationError) -> Self {
        BuildError::FormatEnumeration(e)
    }
}

impl std::error::Error for BuildError {
    fn description(&self) -> &str {
        match *self {
            BuildError::DefaultDevice => "Failed to get default device",
            BuildError::FormatEnumeration(ref err) => err.description(),
            BuildError::Creation(ref err) => err.description(),
        }
    }
}

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

// Checks if the target format has a CPAL sample format equivalent.
//
// If so, we can use the sample format to target a stream format that already has a
// matchingn sample format.
//
// Otherwise we'll just fall back to the default sample format and do a conversionn.
fn cpal_sample_format<S: Any>() -> Option<cpal::SampleFormat> {
    let type_id = TypeId::of::<S>();
    if type_id == TypeId::of::<f32>() {
        Some(cpal::SampleFormat::F32)
    } else if type_id == TypeId::of::<i16>() {
        Some(cpal::SampleFormat::I16)
    } else if type_id == TypeId::of::<u16>() {
        Some(cpal::SampleFormat::U16)
    } else {
        None
    }
}

// Nannou allows the user to optionally specify each part of the stream format.
//
// This function is used to determine whether or not a supported format yielded by CPAL matches
// the requested format parameters specified by the user. If the supported format matches, a
// compatible `Format` is returned.
fn matching_supported_formats(
    mut supported_format: cpal::SupportedFormat,
    sample_format: Option<cpal::SampleFormat>,
    channels: Option<usize>,
    sample_rate: Option<cpal::SampleRate>,
) -> Option<cpal::Format> {
    // Check for a matching sample format.
    if let Some(sample_format) = sample_format {
        if supported_format.data_type != sample_format {
            return None;
        }
    }
    // Check for a matching number of channels.
    //
    // If there are more than enough channels, truncate the `SupportedFormat` to match.
    if let Some(channels) = channels {
        let supported_channels = supported_format.channels as usize;
        if supported_channels < channels {
            return None;
        } else if supported_channels > channels {
            supported_format.channels = channels as u16;
        }
    }
    // Check the sample rate.
    if let Some(sample_rate) = sample_rate {
        if supported_format.min_sample_rate > sample_rate
            || supported_format.max_sample_rate < sample_rate
        {
            return None;
        }
        let mut format = supported_format.with_max_sample_rate();
        format.sample_rate = sample_rate;
        return Some(format);
    }
    Some(supported_format.with_max_sample_rate())
}

// Given some audio device find the supported stream format that best matches the given optional
// format parameters (specified by the user).
fn find_best_matching_format<F>(
    device: &cpal::Device,
    mut sample_format: Option<cpal::SampleFormat>,
    channels: Option<usize>,
    mut sample_rate: Option<cpal::SampleRate>,
    default_format: Option<cpal::Format>,
    supported_formats: F,
) -> Result<Option<cpal::Format>, cpal::FormatsEnumerationError>
where
    F: Fn(&cpal::Device) -> Result<Vec<cpal::SupportedFormat>, cpal::FormatsEnumerationError>,
{
    loop {
        {
            // First, see if the default format satisfies the request.
            if let Some(ref format) = default_format {
                if sample_format == Some(format.data_type)
                    && channels
                        .map(|ch| ch <= format.channels as usize)
                        .unwrap_or(true)
                    && sample_rate == Some(format.sample_rate)
                {
                    return Ok(Some(format.clone()));
                }
            }

            // Otherwise search through all supported formats for compatible formats.
            let stream_formats = supported_formats(device)?.into_iter().filter_map(|fmt| {
                matching_supported_formats(fmt, sample_format, channels, sample_rate)
            });

            // Find the supported format with the most channels (this will always be the target
            // number of channels if some specific target number was specified as all other numbers
            // will have been filtered out already).
            if let Some(format) = stream_formats.max_by_key(|fmt| fmt.channels) {
                return Ok(Some(format));
            }
        }

        // If there are no matching formats with the target sample_format, drop the requirement
        // and we'll do a conversion from the default format instead.
        let default_sample_format = default_format.as_ref().map(|f| f.data_type);
        if sample_format.is_some() && sample_format != default_sample_format {
            sample_format = default_sample_format;
        // Otherwise if nannou's default target sample rate is set because the user didn't
        // specify a sample rate, try and fall back to a supported sample rate in case this was
        // the reason we could not find a supported format.
        } else if sample_rate == Some(cpal::SampleRate(DEFAULT_SAMPLE_RATE)) {
            let cpal_default_sample_rate = default_format.as_ref().map(|fmt| fmt.sample_rate);
            let nannou_default_sample_rate = Some(cpal::SampleRate(DEFAULT_SAMPLE_RATE));
            sample_rate = if cpal_default_sample_rate != nannou_default_sample_rate {
                cpal_default_sample_rate
            } else {
                None
            };
        } else {
            return Ok(None);
        }
    }
}
<|endoftext|>


<commit_before>use std::process::{Command, Stdio, Child};
use std::io::Write;

pub fn spawn(sandbox: &str,
             command: &str,
             syscalls: &str,
             args: &[&str],
             timeout: Option<usize>,
             merge_stderr: bool)
             -> Result<Child, String> {
    let mut cmd = Command::new("sudo");
    cmd.arg("playpen")
       .arg(sandbox)
       .arg("--mount-proc")
       .arg("--user=rust");
    if let Some(x) = timeout {
        cmd.arg(format!("--timeout={}", x));
    }
    cmd.arg(format!("--syscalls-file={}", syscalls))
       .arg("--devices=/dev/urandom:r,/dev/null:w")
       .arg("--memory-limit=128")
       .arg("--")
       .arg(command)
       .args(args)
       .stdin(Stdio::piped())
       .stdout(Stdio::piped());
    if merge_stderr {
        cmd.stderr(Stdio::piped());
    } else {
        cmd.stderr(Stdio::inherit());
    }
    cmd.spawn().map_err(|x| format!("couldn't playpen_exec; {}", x))
}

pub fn exec_wait(sandbox: &str,
                 command: &str,
                 syscalls: &str,
                 args: &[&str],
                 input: &str,
                 timeout: usize)
                 -> Result<String, String> {
    let mut child = try!(spawn(sandbox, command, syscalls, args, Some(timeout), true));
    if let Some(ref mut x) = child.stdin {
        try!(x.write_all(input.as_bytes())
              .map_err(|x| format!("couldn't write to stdin; {}", x)));
    } else {
        return Err("no stdin?".to_owned());
    }
    let output = try!(child.wait_with_output()
                           .map_err(|x| format!("wait_with_output failed; {}", x)));
    Ok({
        let mut out = String::from_utf8_lossy(&output.stdout).into_owned();
        if !out.ends_with("\n") { out.push_str("\n"); }
        out.push_str(&*String::from_utf8_lossy(&output.stderr));
        out})
}
<commit_msg>Change sandboxed username<commit_after>use std::process::{Command, Stdio, Child};
use std::io::Write;

pub fn spawn(sandbox: &str,
             command: &str,
             syscalls: &str,
             args: &[&str],
             timeout: Option<usize>,
             merge_stderr: bool)
             -> Result<Child, String> {
    let mut cmd = Command::new("sudo");
    cmd.arg("playpen")
       .arg(sandbox)
       .arg("--mount-proc")
       .arg("--user=eval");
    if let Some(x) = timeout {
        cmd.arg(format!("--timeout={}", x));
    }
    cmd.arg(format!("--syscalls-file={}", syscalls))
       .arg("--devices=/dev/urandom:r,/dev/null:w")
       .arg("--memory-limit=128")
       .arg("--")
       .arg(command)
       .args(args)
       .stdin(Stdio::piped())
       .stdout(Stdio::piped());
    if merge_stderr {
        cmd.stderr(Stdio::piped());
    } else {
        cmd.stderr(Stdio::inherit());
    }
    cmd.spawn().map_err(|x| format!("couldn't playpen_exec; {}", x))
}

pub fn exec_wait(sandbox: &str,
                 command: &str,
                 syscalls: &str,
                 args: &[&str],
                 input: &str,
                 timeout: usize)
                 -> Result<String, String> {
    let mut child = try!(spawn(sandbox, command, syscalls, args, Some(timeout), true));
    if let Some(ref mut x) = child.stdin {
        try!(x.write_all(input.as_bytes())
              .map_err(|x| format!("couldn't write to stdin; {}", x)));
    } else {
        return Err("no stdin?".to_owned());
    }
    let output = try!(child.wait_with_output()
                           .map_err(|x| format!("wait_with_output failed; {}", x)));
    Ok({
        let mut out = String::from_utf8_lossy(&output.stdout).into_owned();
        if !out.ends_with("\n") { out.push_str("\n"); }
        out.push_str(&*String::from_utf8_lossy(&output.stderr));
        out})
}
<|endoftext|>


<commit_before>use crate::utils::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
    Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind,
};
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::{Ident, Symbol};
use std::cmp::Ordering;

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: Symbol,
    span: Span,
    len: usize,
    exemptions: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 > threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => {
                if !pat.span.from_expansion() {
                    self.check_ident(ident);
                }
            },
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

#[must_use]
fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
    for &list in ALLOWED_TO_BE_SIMILAR {
        if allowed_to_be_similar(interned_name, list) {
            return Some(list);
        }
    }
    None
}

#[must_use]
fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if allowed_to_be_similar(&interned_name, existing_name.exemptions) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1
                        || levenstein_not_1(&interned_name, &existing_name.interned.as_str())
                    {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1
                        || levenstein_not_1(&existing_name.interned.as_str(), &interned_name)
                    {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let interned_str = existing_name.interned.as_str();
                    let mut existing_chars = interned_str.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            exemptions: get_exemptions(&interned_name).unwrap_or(&[]),
            interned: ident.name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attrs.iter().any(|attr| attr.has_name(sym!(test))) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<commit_msg>rustc_ast: Do not panic by default when visiting macro calls<commit_after>use crate::utils::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
    Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind,
};
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::{Ident, Symbol};
use std::cmp::Ordering;

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: Symbol,
    span: Span,
    len: usize,
    exemptions: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 > threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => {
                if !pat.span.from_expansion() {
                    self.check_ident(ident);
                }
            },
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
}

#[must_use]
fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
    for &list in ALLOWED_TO_BE_SIMILAR {
        if allowed_to_be_similar(interned_name, list) {
            return Some(list);
        }
    }
    None
}

#[must_use]
fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if allowed_to_be_similar(&interned_name, existing_name.exemptions) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1
                        || levenstein_not_1(&interned_name, &existing_name.interned.as_str())
                    {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1
                        || levenstein_not_1(&existing_name.interned.as_str(), &interned_name)
                    {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let interned_str = existing_name.interned.as_str();
                    let mut existing_chars = interned_str.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            exemptions: get_exemptions(&interned_name).unwrap_or(&[]),
            interned: ident.name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attrs.iter().any(|attr| attr.has_name(sym!(test))) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<|endoftext|>