<commit_before>//! File holding the Pushable trait
//!
//! Author: [Boris](mailto:boris@humanenginuity.com)
//! Version: 1.0
//!
//! ## Release notes
//! - v1.0 : creation

// =======================================================================
// LIBRARY IMPORTS
// =======================================================================
use serde_json;
use serde_json::Value;

// =======================================================================
// TRAIT DEFINITION
// =======================================================================
pub trait Pushable<T> {
    fn push(&mut self, value: T) -> &mut Self;
}

// =======================================================================
// TRAIT IMPLEMENTATION
// =======================================================================
/// Implements `Pushable<T>` for `Vec<T>`
impl<T> Pushable<T> for Vec<T> {
    fn push(&mut self, new_value: T) -> &mut Self {
        self.push(new_value);
        self
    }
}

/// Implements `Pushable` for `serde_json::Value`
///
/// - If `self` is anything but a `Value::Array` => transforms to an Array containing the existing value and appends the new value
/// - If `self` is a `Value::Array` => appends the new value
impl Pushable<Value> for Value {
    fn push(&mut self, new_value: Value) -> &mut Self {
        let mut vect = Vec::new();

        match *self {
            Value::Array(ref mut existing_vect) => vect.append(existing_vect),
            Value::Null => (), // do nothing
            ref existing_value @ _ => vect.push(existing_value.clone()),
        }
        vect.push(new_value);
        ::std::mem::replace(self, Value::Array(vect));
        self
    }
}

/// Allow to push a String using `serde_json::from_str()`
/// It first try to use `serde_json::from_str()` on the String. If it fails, it pushes a new `Value::String()` instead
impl Pushable<String> for Value {
    fn push(&mut self, new_value: String) -> &mut Self {
        self.push(new_value.as_str())
    }
}

/// Allow to push a &str using `serde_json::from_str()`
/// It first try to use `serde_json::from_str()` on the String. If it fails, it pushes a new `Value::String()` instead
impl<'s> Pushable<&'s str> for Value {
    fn push(&mut self, new_value: &'s str) -> &mut Self {
        let value : Result<Value, _> = serde_json::from_str(new_value);

        if value.is_err() {
            self.push(Value::String(new_value.to_string()));
        } else {
            self.push(value.unwrap());
        }
        self
    }
}

/// Allow to push Result<Value, _> (sugar for pushing `serde_json::from_xxx()`)
/// Panics if conversion failed
impl Pushable<Result<Value, serde_json::Error>> for Value {
    fn push(&mut self, new_value: Result<Value, serde_json::Error>) -> &mut Self {
        if new_value.is_err() {
            panic!("::amiwo::pushable<Result<V, E>>::push::error unable to push invalid value {}", &new_value.unwrap_err());
        } else {
            self.push(new_value.unwrap());
        }
        self
    }
}

// =======================================================================
// UNIT TESTS
// =======================================================================
#[cfg(test)]
mod tests {
    #![allow(non_snake_case)]

    use super::Pushable;

    #[test]
    fn Pushable_test_value() {
        let mut x = json!("a");
        x.push("b");
        assert_eq!(x, json!(["a", "b"]));

        let mut x = json!(1);
        x.push("b");
        assert_eq!(x, json!([1, "b"]));

        let mut x = json!(true);
        x.push("b");
        assert_eq!(x, json!([true, "b"]));

        let mut x = json!(["a", "b"]);
        x.push("c");
        assert_eq!(x, json!(["a", "b", "c"]));

        let mut x = json!({
            "key1": "value1",
            "key2": "value2"
        });
        x.push("c");
        assert_eq!(x, json!([
            {
                "key1": "value1",
                "key2": "value2"
            },
            "c"
        ]));
    }
}<commit_msg>Pushable > 'push()' works properly on Value::Null<commit_after>//! File holding the Pushable trait
//!
//! Author: [Boris](mailto:boris@humanenginuity.com)
//! Version: 1.0
//!
//! ## Release notes
//! - v1.0 : creation

// =======================================================================
// LIBRARY IMPORTS
// =======================================================================
use serde_json;
use serde_json::Value;

// =======================================================================
// TRAIT DEFINITION
// =======================================================================
pub trait Pushable<T> {
    fn push(&mut self, value: T) -> &mut Self;
}

// =======================================================================
// TRAIT IMPLEMENTATION
// =======================================================================
/// Implements `Pushable<T>` for `Vec<T>`
impl<T> Pushable<T> for Vec<T> {
    fn push(&mut self, new_value: T) -> &mut Self {
        self.push(new_value);
        self
    }
}

/// Implements `Pushable` for `serde_json::Value`
///
/// - If `self` is anything but a `Value::Array` => transforms to an Array containing the existing value and appends the new value
/// - If `self` is a `Value::Array` => appends the new value
impl Pushable<Value> for Value {
    fn push(&mut self, new_value: Value) -> &mut Self {
        if self.is_null() {
            ::std::mem::replace(self, new_value);
            self
        } else {
            let mut vect = Vec::new();
            match *self {
                Value::Array(ref mut existing_vect) => vect.append(existing_vect),
                Value::Null => (), // do nothing
                ref existing_value @ _ => vect.push(existing_value.clone()),
            }
            vect.push(new_value);
            ::std::mem::replace(self, Value::Array(vect));
            self
        }
    }
}

/// Allow to push a String using `serde_json::from_str()`
/// It first try to use `serde_json::from_str()` on the String. If it fails, it pushes a new `Value::String()` instead
impl Pushable<String> for Value {
    fn push(&mut self, new_value: String) -> &mut Self {
        self.push(new_value.as_str())
    }
}

/// Allow to push a &str using `serde_json::from_str()`
/// It first try to use `serde_json::from_str()` on the String. If it fails, it pushes a new `Value::String()` instead
impl<'s> Pushable<&'s str> for Value {
    fn push(&mut self, new_value: &'s str) -> &mut Self {
        let value : Result<Value, _> = serde_json::from_str(new_value);

        if value.is_err() {
            self.push(Value::String(new_value.to_string()));
        } else {
            self.push(value.unwrap());
        }
        self
    }
}

/// Allow to push Result<Value, _> (sugar for pushing `serde_json::from_xxx()`)
/// Panics if conversion failed
impl Pushable<Result<Value, serde_json::Error>> for Value {
    fn push(&mut self, new_value: Result<Value, serde_json::Error>) -> &mut Self {
        if new_value.is_err() {
            panic!("::amiwo::pushable<Result<V, E>>::push::error unable to push invalid value {}", &new_value.unwrap_err());
        } else {
            self.push(new_value.unwrap());
        }
        self
    }
}

// =======================================================================
// UNIT TESTS
// =======================================================================
#[cfg(test)]
mod tests {
    #![allow(non_snake_case)]

    use super::Pushable;
    use serde_json::Value;

    #[test]
    fn Pushable_test_value() {
        let mut x = Value::Null;
        x.push("a");
        assert!(!x.is_array());
        assert_eq!(x, json!("a"));

        let mut x = json!("a");
        x.push("b");
        assert_eq!(x, json!(["a", "b"]));

        let mut x = json!(1);
        x.push("b");
        assert_eq!(x, json!([1, "b"]));

        let mut x = json!(true);
        x.push("b");
        assert_eq!(x, json!([true, "b"]));

        let mut x = json!(["a", "b"]);
        x.push("c");
        assert_eq!(x, json!(["a", "b", "c"]));

        let mut x = json!({
            "key1": "value1",
            "key2": "value2"
        });
        x.push("c");
        assert_eq!(x, json!([
            {
                "key1": "value1",
                "key2": "value2"
            },
            "c"
        ]));
    }
}<|endoftext|>


<commit_before>use std::io::{self, Write};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

pub fn heading(content: &str) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);

    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Cyan))) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match writeln!(&mut stdout, "\n{}\n{}", content, underline(content, "=")) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match stdout.reset() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn subheading(content: &str) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);

    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Cyan))) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match writeln!(&mut stdout, "\n{}\n{}", content, underline(content, "-")) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match stdout.reset() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn progress(content: String) {
    let mut stdout = io::stdout();

    match writeln!(&mut stdout, "\n{}", content) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn info(content: String) {
    let mut stdout = io::stdout();

    match writeln!(&mut stdout, "\n{}", content) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn error(content: String) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);

    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red))) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match writeln!(&mut stdout, "\n{}", content) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match stdout.reset() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn list(items: Vec<&str>) {
    let mut stdout = io::stdout();

    match writeln!(&mut stdout, "") {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    items
        .iter()
        .for_each(|content| match writeln!(&mut stdout, "- {}", content) {
            Ok(_) => (),
            Err(error) => panic!("There was a problem: {:?}", error),
        })
}

pub fn pause() {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);

    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow))) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    loop {
        let prompt = "Enter 'Done' when ready to continue:";

        match writeln!(&mut stdout, "\n{}", prompt) {
            Ok(_) => (),
            Err(error) => panic!("There was a problem: {:?}", error),
        }

        let mut input = String::new();

        match io::stdin().read_line(&mut input) {
            Ok(_) => (),
            Err(error) => panic!("There was a problem: {:?}", error),
        }

        if input.contains("Done") {
            match stdout.reset() {
                Ok(_) => (),
                Err(error) => panic!("There was a problem: {:?}", error),
            }

            break;
        } else {
            continue;
        }
    }

    for x in (0..100).step_by(0) {
        println!("{}", x);
    };
}

fn underline(content: &str, symbol: &str) -> String {
                content.chars().map(|_| symbol).collect()
}

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

    #[test]
    fn test_underline() {
        assert_eq!(underline("Some line", "="), "...");
        assert_eq!(underline("Another line", "-"), "...");
    }
}
<commit_msg>Fix test<commit_after>use std::io::{self, Write};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

pub fn heading(content: &str) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);

    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Cyan))) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match writeln!(&mut stdout, "\n{}\n{}", content, underline(content, "=")) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match stdout.reset() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn subheading(content: &str) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);

    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Cyan))) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match writeln!(&mut stdout, "\n{}\n{}", content, underline(content, "-")) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match stdout.reset() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn progress(content: String) {
    let mut stdout = io::stdout();

    match writeln!(&mut stdout, "\n{}", content) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn info(content: String) {
    let mut stdout = io::stdout();

    match writeln!(&mut stdout, "\n{}", content) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn error(content: String) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);

    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red))) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match writeln!(&mut stdout, "\n{}", content) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    match stdout.reset() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}

pub fn list(items: Vec<&str>) {
    let mut stdout = io::stdout();

    match writeln!(&mut stdout, "") {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    items
        .iter()
        .for_each(|content| match writeln!(&mut stdout, "- {}", content) {
            Ok(_) => (),
            Err(error) => panic!("There was a problem: {:?}", error),
        })
}

pub fn pause() {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);

    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow))) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    loop {
        let prompt = "Enter 'Done' when ready to continue:";

        match writeln!(&mut stdout, "\n{}", prompt) {
            Ok(_) => (),
            Err(error) => panic!("There was a problem: {:?}", error),
        }

        let mut input = String::new();

        match io::stdin().read_line(&mut input) {
            Ok(_) => (),
            Err(error) => panic!("There was a problem: {:?}", error),
        }

        if input.contains("Done") {
            match stdout.reset() {
                Ok(_) => (),
                Err(error) => panic!("There was a problem: {:?}", error),
            }

            break;
        } else {
            continue;
        }
    }

    for x in (0..100).step_by(0) {
        println!("{}", x);
    };
}

fn underline(content: &str, symbol: &str) -> String {
                content.chars().map(|_| symbol).collect()
}

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

    #[test]
    fn test_underline() {
        assert_eq!(underline("Some line", "="), "=========");
        assert_eq!(underline("Another line", "-"), "------------");
    }
}
<|endoftext|>


<commit_before>extern crate open_location_code;
extern crate geo;

use std::vec::Vec;

use open_location_code::{is_valid, is_short, is_full};
use open_location_code::{encode, decode};
use open_location_code::{shorten, recover_nearest};

use geo::Point;

mod csv_reader;

use csv_reader::CSVReader;

/// CSVReader is written to swallow errors; as such, we might "pass" tests because we didn't
/// actually run any!  Thus, we use 'tested' below to count # lines read and hence to assert that
/// > 0 tests were executed.
///
/// We could probably take it a little further, and assert that tested was >= # tests in the file
/// (allowing tests to be added, but assuming # tests will never be reduced).

#[test]
fn is_valid_test() {
    let mut tested = 0;
    for line in CSVReader::new("validityTests.csv") {
        let cols: Vec<&str> = line.split(',').collect();
        let code = cols[0];
        let _valid = cols[1] == "true";
        let _short = cols[2] == "true";
        let _full = cols[3] == "true";

        assert_eq!(is_valid(code), _valid, "valid for code: {}", code);
        assert_eq!(is_short(code), _short, "short for code: {}", code);
        assert_eq!(is_full(code), _full, "full for code: {}", code);

        tested += 1;
    }
    assert!(tested > 0);
}

#[test]
fn encode_decode_test() {
    let mut tested = 0;
    for line in CSVReader::new("encodingTests.csv") {
        let cols: Vec<&str> = line.split(',').collect();
        let code = cols[0];
        let lat = cols[1].parse::<f64>().unwrap();
        let lng = cols[2].parse::<f64>().unwrap();
        let latlo = cols[3].parse::<f64>().unwrap();
        let lnglo = cols[4].parse::<f64>().unwrap();
        let lathi = cols[5].parse::<f64>().unwrap();
        let lnghi = cols[6].parse::<f64>().unwrap();

        let codearea = decode(code).unwrap();
        assert_eq!(
            encode(Point::new(lng, lat), codearea.code_length),
            code,
            "encoding lat={},lng={}", lat, lng
        );
        assert!((latlo - codearea.south).abs() < 1e-10f64);
        assert!((lathi - codearea.north).abs() < 1e-10f64);
        assert!((lnglo - codearea.west).abs() < 1e-10f64);
        assert!((lnghi - codearea.east).abs() < 1e-10f64);

        tested += 1;
    }
    assert!(tested > 0);
}

#[test]
fn shorten_recovery_test() {
    let mut tested = 0;
    for line in CSVReader::new("shortCodeTests.csv") {
        let cols: Vec<&str> = line.split(',').collect();
        let full_code = cols[0];
        let lat = cols[1].parse::<f64>().unwrap();
        let lng = cols[2].parse::<f64>().unwrap();
        let short_code = cols[3];

        assert_eq!(shorten(full_code, Point::new(lng, lat)).unwrap(), short_code, "shorten");
        assert_eq!(
            recover_nearest(short_code, Point::new(lng, lat)),
            Ok(full_code.to_string()),
            "recover"
        );

        tested += 1;
    }

    assert!(tested > 0);
}

<commit_msg>Update all_test.rs<commit_after>extern crate open_location_code;
extern crate geo;

use std::vec::Vec;

use open_location_code::{is_valid, is_short, is_full};
use open_location_code::{encode, decode};
use open_location_code::{shorten, recover_nearest};

use geo::Point;

mod csv_reader;

use csv_reader::CSVReader;

/// CSVReader is written to swallow errors; as such, we might "pass" tests because we didn't
/// actually run any!  Thus, we use 'tested' below to count # lines read and hence to assert that
/// > 0 tests were executed.
///
/// We could probably take it a little further, and assert that tested was >= # tests in the file
/// (allowing tests to be added, but assuming # tests will never be reduced).

#[test]
fn is_valid_test() {
    let mut tested = 0;
    for line in CSVReader::new("validityTests.csv") {
        let cols: Vec<&str> = line.split(',').collect();
        let code = cols[0];
        let _valid = cols[1] == "true";
        let _short = cols[2] == "true";
        let _full = cols[3] == "true";

        assert_eq!(is_valid(code), _valid, "valid for code: {}", code);
        assert_eq!(is_short(code), _short, "short for code: {}", code);
        assert_eq!(is_full(code), _full, "full for code: {}", code);

        tested += 1;
    }
    assert!(tested > 0);
}

#[test]
fn encode_decode_test() {
    let mut tested = 0;
    for line in CSVReader::new("encodingTests.csv") {
        let cols: Vec<&str> = line.split(',').collect();
        let code = cols[0];
        let lat = cols[1].parse::<f64>().unwrap();
        let lng = cols[2].parse::<f64>().unwrap();
        let latlo = cols[3].parse::<f64>().unwrap();
        let lnglo = cols[4].parse::<f64>().unwrap();
        let lathi = cols[5].parse::<f64>().unwrap();
        let lnghi = cols[6].parse::<f64>().unwrap();

        let codearea = decode(code).unwrap();
        assert_eq!(
            encode(Point::new(lng, lat), codearea.code_length),
            code,
            "encoding lat={},lng={}", lat, lng
        );
        assert!((latlo - codearea.south).abs() < 1e-10f64);
        assert!((lathi - codearea.north).abs() < 1e-10f64);
        assert!((lnglo - codearea.west).abs() < 1e-10f64);
        assert!((lnghi - codearea.east).abs() < 1e-10f64);

        tested += 1;
    }
    assert!(tested > 0);
}

#[test]
fn shorten_recovery_test() {
    let mut tested = 0;
    for line in CSVReader::new("shortCodeTests.csv") {
        let cols: Vec<&str> = line.split(',').collect();
        let full_code = cols[0];
        let lat = cols[1].parse::<f64>().unwrap();
        let lng = cols[2].parse::<f64>().unwrap();
        let short_code = cols[3];
        let test_type = cols[4];

        if test_type == 'B' || test_type == 'S' {
            assert_eq!(shorten(full_code, Point::new(lng, lat)).unwrap(), short_code, "shorten");
        }
        if test_type == 'B' || test_type == 'R' {
            assert_eq!(
                recover_nearest(short_code, Point::new(lng, lat)),
                Ok(full_code.to_string()),
                "recover"
            );
        }

        tested += 1;
    }

    assert!(tested > 0);
}

<|endoftext|>


<commit_before>//! types for working with raw big-endian bytes

/// A trait for font scalars.
///
/// This is an internal trait for encoding and decoding big-endian bytes.
///
/// You do not need to implement this trait directly; it is an implemention
/// detail of the [`BigEndian`] wrapper.
pub trait Scalar {
    /// The raw byte representation of this type.
    type Raw: Copy + AsRef<[u8]>;

    /// Create an instance of this type from raw big-endian bytes
    fn from_raw(raw: Self::Raw) -> Self;
    /// Encode this type as raw big-endian bytes
    fn to_raw(self) -> Self::Raw;
}

/// A trait for types that have a known, constant size.
pub trait FixedSize: Sized {
    /// The size of this type, in bytes.
    const RAW_BYTE_LEN: usize;
}

/// A trait for types that can be represented as raw bytes.
pub trait ReadScalar: FixedSize {
    fn read(bytes: &[u8]) -> Option<Self>;
}

/// A wrapper around raw big-endian bytes for some type.
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct BigEndian<T: Scalar>(pub(crate) T::Raw);

impl<T: Scalar> BigEndian<T> {
    /// construct a new BigEndian<T> from raw bytes
    pub fn new(raw: T::Raw) -> BigEndian<T> {
        BigEndian(raw)
    }
    /// Read a copy of this type from raw bytes.
    pub fn get(self) -> T {
        T::from_raw(self.0)
    }

    /// Set the value, overwriting the bytes.
    pub fn set(&mut self, value: T) {
        self.0 = value.to_raw();
    }

    /// Get the raw big-endian bytes.
    pub fn be_bytes(&self) -> &[u8] {
        self.0.as_ref()
    }
}

impl<T: Scalar> From<T> for BigEndian<T> {
    #[inline]
    fn from(val: T) -> Self {
        BigEndian(val.to_raw())
    }
}

impl<T: Scalar + Default> Default for BigEndian<T> {
    fn default() -> Self {
        Self::from(T::default())
    }
}

impl<T: Scalar + Copy + PartialEq> PartialEq<T> for BigEndian<T> {
    fn eq(&self, other: &T) -> bool {
        self.get() == *other
    }
}

// these following impls are an elaborate way to impl ReadScalar for BigEndian<T>
impl<const N: usize> FixedSize for [u8; N] {
    const RAW_BYTE_LEN: usize = N;
}

impl<const N: usize> ReadScalar for [u8; N] {
    #[inline]
    fn read(bytes: &[u8]) -> Option<Self> {
        bytes.try_into().ok()
    }
}

impl<T> ReadScalar for BigEndian<T>
where
    T: Scalar + FixedSize,
    <T as Scalar>::Raw: ReadScalar,
{
    #[inline]
    fn read(bytes: &[u8]) -> Option<Self> {
        T::Raw::read(bytes).map(BigEndian)
    }
}

// and then we can impl ReadScalar for T based on the impl for BigEndian<T>
impl<T> ReadScalar for T
where
    T: Scalar + FixedSize,
    <T as Scalar>::Raw: ReadScalar,
{
    #[inline]
    fn read(bytes: &[u8]) -> Option<Self> {
        BigEndian::<T>::read(bytes).map(BigEndian::get)
    }
}

// and impl FixedSized for T based on the impl for the arrays
impl<T> FixedSize for T
where
    T: Scalar,
    <T as Scalar>::Raw: FixedSize,
{
    const RAW_BYTE_LEN: usize = <T as Scalar>::Raw::RAW_BYTE_LEN;
}

// and impl FixedSized for BigEndian<T> based on the impl forr T
impl<T: Scalar + FixedSize> FixedSize for BigEndian<T> {
    const RAW_BYTE_LEN: usize = T::RAW_BYTE_LEN;
}

/// An internal macro for implementing the `RawType` trait.
#[macro_export]
macro_rules! newtype_scalar {
    ($ty:ident, $raw:ty) => {
        impl $crate::raw::Scalar for $ty {
            type Raw = $raw;
            fn to_raw(self) -> $raw {
                self.0.to_raw()
            }

            fn from_raw(raw: $raw) -> Self {
                Self($crate::raw::Scalar::from_raw(raw))
            }
        }
    };
}

macro_rules! int_scalar {
    ($ty:ty, $raw:ty) => {
        impl crate::raw::Scalar for $ty {
            type Raw = $raw;
            fn to_raw(self) -> $raw {
                self.to_be_bytes()
            }

            fn from_raw(raw: $raw) -> $ty {
                Self::from_be_bytes(raw)
            }
        }
    };
}

int_scalar!(u8, [u8; 1]);
int_scalar!(i8, [u8; 1]);
int_scalar!(u16, [u8; 2]);
int_scalar!(i16, [u8; 2]);
int_scalar!(u32, [u8; 4]);
int_scalar!(i32, [u8; 4]);
int_scalar!(i64, [u8; 8]);
int_scalar!(crate::Uint24, [u8; 3]);

impl<T: std::fmt::Debug + Scalar + Copy> std::fmt::Debug for BigEndian<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.get().fmt(f)
    }
}

impl<T: std::fmt::Display + Scalar + Copy> std::fmt::Display for BigEndian<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.get().fmt(f)
    }
}
<commit_msg>Improve docs of Scalar/FixedSize/ReadScalar traits<commit_after>//! types for working with raw big-endian bytes

/// A trait for font scalars.
///
/// This is an internal trait for encoding and decoding big-endian bytes.
///
/// You do not need to implement this trait directly; it is an implemention
/// detail of the [`BigEndian`] wrapper.
pub trait Scalar {
    /// The raw byte representation of this type.
    type Raw: Copy + AsRef<[u8]>;

    /// Create an instance of this type from raw big-endian bytes
    fn from_raw(raw: Self::Raw) -> Self;
    /// Encode this type as raw big-endian bytes
    fn to_raw(self) -> Self::Raw;
}

/// A trait for types that have a known, constant size.
pub trait FixedSize: Sized {
    /// The raw size of this type, in bytes.
    ///
    /// This is the size required to represent this type in a font file, which
    /// may differ from the size of the native type:
    ///
    /// ```
    /// # use font_types::{FixedSize, Offset24};
    /// assert_eq!(std::mem::size_of::<u16>(), u16::RAW_BYTE_LEN);
    /// assert_eq!(Offset24::RAW_BYTE_LEN, 3);
    /// assert_eq!(std::mem::size_of::<Offset24>(), 4);
    /// ```
    const RAW_BYTE_LEN: usize;
}

/// A trait for types that can be read from raw bytes.
///
/// This is a generalization that gives us a failable read method for all our
/// `Scalar` types, as well as their `BigEndian` representations.
///
/// You should not need to implement this trait; it is provided automatically
/// when you implement [`Scalar`].
pub trait ReadScalar: FixedSize {
    /// Interpret the provided bytes as `Self`, if they are the right length.
    ///
    /// This should use all the provided bytes; bounds checking is performed upstream.
    fn read(bytes: &[u8]) -> Option<Self>;
}

/// A wrapper around raw big-endian bytes for some type.
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct BigEndian<T: Scalar>(pub(crate) T::Raw);

impl<T: Scalar> BigEndian<T> {
    /// construct a new BigEndian<T> from raw bytes
    pub fn new(raw: T::Raw) -> BigEndian<T> {
        BigEndian(raw)
    }
    /// Read a copy of this type from raw bytes.
    pub fn get(self) -> T {
        T::from_raw(self.0)
    }

    /// Set the value, overwriting the bytes.
    pub fn set(&mut self, value: T) {
        self.0 = value.to_raw();
    }

    /// Get the raw big-endian bytes.
    pub fn be_bytes(&self) -> &[u8] {
        self.0.as_ref()
    }
}

impl<T: Scalar> From<T> for BigEndian<T> {
    #[inline]
    fn from(val: T) -> Self {
        BigEndian(val.to_raw())
    }
}

impl<T: Scalar + Default> Default for BigEndian<T> {
    fn default() -> Self {
        Self::from(T::default())
    }
}

impl<T: Scalar + Copy + PartialEq> PartialEq<T> for BigEndian<T> {
    fn eq(&self, other: &T) -> bool {
        self.get() == *other
    }
}

// these following impls are an elaborate way to impl ReadScalar for BigEndian<T>
impl<const N: usize> FixedSize for [u8; N] {
    const RAW_BYTE_LEN: usize = N;
}

impl<const N: usize> ReadScalar for [u8; N] {
    #[inline]
    fn read(bytes: &[u8]) -> Option<Self> {
        bytes.try_into().ok()
    }
}

impl<T> ReadScalar for BigEndian<T>
where
    T: Scalar + FixedSize,
    <T as Scalar>::Raw: ReadScalar,
{
    #[inline]
    fn read(bytes: &[u8]) -> Option<Self> {
        T::Raw::read(bytes).map(BigEndian)
    }
}

// and then we can impl ReadScalar for T based on the impl for BigEndian<T>
impl<T> ReadScalar for T
where
    T: Scalar + FixedSize,
    <T as Scalar>::Raw: ReadScalar,
{
    #[inline]
    fn read(bytes: &[u8]) -> Option<Self> {
        BigEndian::<T>::read(bytes).map(BigEndian::get)
    }
}

// and impl FixedSized for T based on the impl for the arrays
impl<T> FixedSize for T
where
    T: Scalar,
    <T as Scalar>::Raw: FixedSize,
{
    const RAW_BYTE_LEN: usize = <T as Scalar>::Raw::RAW_BYTE_LEN;
}

// and impl FixedSized for BigEndian<T> based on the impl forr T
impl<T: Scalar + FixedSize> FixedSize for BigEndian<T> {
    const RAW_BYTE_LEN: usize = T::RAW_BYTE_LEN;
}

/// An internal macro for implementing the `RawType` trait.
#[macro_export]
macro_rules! newtype_scalar {
    ($ty:ident, $raw:ty) => {
        impl $crate::raw::Scalar for $ty {
            type Raw = $raw;
            fn to_raw(self) -> $raw {
                self.0.to_raw()
            }

            fn from_raw(raw: $raw) -> Self {
                Self($crate::raw::Scalar::from_raw(raw))
            }
        }
    };
}

macro_rules! int_scalar {
    ($ty:ty, $raw:ty) => {
        impl crate::raw::Scalar for $ty {
            type Raw = $raw;
            fn to_raw(self) -> $raw {
                self.to_be_bytes()
            }

            fn from_raw(raw: $raw) -> $ty {
                Self::from_be_bytes(raw)
            }
        }
    };
}

int_scalar!(u8, [u8; 1]);
int_scalar!(i8, [u8; 1]);
int_scalar!(u16, [u8; 2]);
int_scalar!(i16, [u8; 2]);
int_scalar!(u32, [u8; 4]);
int_scalar!(i32, [u8; 4]);
int_scalar!(i64, [u8; 8]);
int_scalar!(crate::Uint24, [u8; 3]);

impl<T: std::fmt::Debug + Scalar + Copy> std::fmt::Debug for BigEndian<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.get().fmt(f)
    }
}

impl<T: std::fmt::Display + Scalar + Copy> std::fmt::Display for BigEndian<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.get().fmt(f)
    }
}
<|endoftext|>


<commit_before>use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use serde_json;
use std::ffi::OsStr;
use std::io::{BufReader, ErrorKind as IoErrorKind, Read, Result as IoResult};
use std::process::{Child, Command, Stdio};
use super::{AudioSource, AudioType, DcaError, DcaMetadata, VoiceError};
use internal::prelude::*;

use std::fs::File;
use std::result::Result as StdResult;

struct ChildContainer(Child);

impl Read for ChildContainer {
    fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> {
        self.0.stdout.as_mut().unwrap().read(buffer)
    }
}

struct InputSource<R: Read + Send + 'static> {
    stereo: bool,
    reader: R,
    kind: AudioType,
}

impl<R: Read + Send> AudioSource for InputSource<R> {
    fn is_stereo(&mut self) -> bool { self.stereo }

    fn get_type(&self) -> AudioType { self.kind.clone() }

    fn read_pcm_frame(&mut self, buffer: &mut [i16]) -> Option<usize> {
        for (i, v) in buffer.iter_mut().enumerate() {
            *v = match self.reader.read_i16::<LittleEndian>() {
                Ok(v) => v,
                Err(ref e) => {
                    return if e.kind() == IoErrorKind::UnexpectedEof {
                        Some(i)
                    } else {
                        None
                    }
                },
            }
        }

        Some(buffer.len())
    }

    fn read_opus_frame(&mut self) -> Option<Vec<u8>> {
        match self.reader.read_i16::<LittleEndian>() {
            Ok(size) => {
                let mut frame = Vec::with_capacity(size as usize);

                {
                    let reader = self.reader.by_ref();

                    if let Err(_) = reader.take(size as u64).read_to_end(&mut frame) {
                        return None;
                    }
                }

                Some(frame)
            },
            Err(ref e) => {
                return if e.kind() == IoErrorKind::UnexpectedEof {
                    Some(Vec::new())
                } else {
                    None
                }
            },
        }
    }
}

/// Opens an audio file through `ffmpeg` and creates an audio source.
pub fn ffmpeg<P: AsRef<OsStr>>(path: P) -> Result<Box<AudioSource>> {
    let path = path.as_ref();

    // Will fail if the path is not to a file on the fs. Likely a YouTube URI.
    let is_stereo = is_stereo(path).unwrap_or(false);
    let stereo_val = if is_stereo { "2" } else { "1" };

    let args = [
        "-f",
        "s16le",
        "-ac",
        stereo_val,
        "-ar",
        "48000",
        "-acodec",
        "pcm_s16le",
        "-",
    ];

    let command = Command::new("ffmpeg")
        .arg("-i")
        .arg(path)
        .args(&args)
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    Ok(pcm(is_stereo, ChildContainer(command)))
}

/// Creates a streamed audio source from a DCA file.
/// Currently only accepts the DCA1 format.
pub fn dca<P: AsRef<OsStr>>(path: P) -> StdResult<Box<AudioSource>, DcaError> {
    let file = File::open(path.as_ref()).map_err(DcaError::IoError)?;

    let mut reader = BufReader::new(file);

    let mut header = [0u8; 4];

    // Read in the magic number to verify it's a DCA file.
    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    if header != b"DCA1"[..] {
        return Err(DcaError::InvalidHeader);
    }

    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    let size = (&header[..]).read_i32::<LittleEndian>().unwrap();

    // Sanity check
    if size < 2 {
        return Err(DcaError::InvalidSize(size));
    }

    info!("Size: {}", size);

    let mut raw_json = Vec::with_capacity(size as usize);

    {
        let json_reader = reader.by_ref();
        json_reader.take(size as u64).read_to_end(&mut raw_json).map_err(DcaError::IoError)?;
    }

    info!("Raw JSON: {:?}", raw_json);

    let metadata = serde_json::from_slice::<DcaMetadata>(raw_json.as_slice()).map_err(DcaError::InvalidMetadata)?;

    Ok(opus(metadata.is_stereo(), reader))
}

/// Creates an Opus audio source.
pub fn opus<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader: reader,
        kind: AudioType::Opus
    })
}

/// Creates a PCM audio source.
pub fn pcm<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader: reader,
        kind: AudioType::Pcm
    })
}

/// Creates a streamed audio source with `youtube-dl` and `ffmpeg`.
pub fn ytdl(uri: &str) -> Result<Box<AudioSource>> {
    let args = [
        "-f",
        "webm[abr>0]/bestaudio/best",
        "--no-playlist",
        "--print-json",
        "--skip-download",
        uri,
    ];

    let out = Command::new("youtube-dl")
        .args(&args)
        .stdin(Stdio::null())
        .output()?;

    if !out.status.success() {
        return Err(Error::Voice(VoiceError::YouTubeDLRun(out)));
    }

    let value = serde_json::from_reader(&out.stdout[..])?;
    let mut obj = match value {
        Value::Object(obj) => obj,
        other => return Err(Error::Voice(VoiceError::YouTubeDLProcessing(other))),
    };

    let uri = match obj.remove("url") {
        Some(v) => {
            match v {
                Value::String(uri) => uri,
                other => return Err(Error::Voice(VoiceError::YouTubeDLUrl(other))),
            }
        },
        None => return Err(Error::Voice(VoiceError::YouTubeDLUrl(Value::Object(obj)))),
    };

    ffmpeg(&uri)
}

fn is_stereo(path: &OsStr) -> Result<bool> {
    let args = ["-v", "quiet", "-of", "json", "-show-streams", "-i"];

    let out = Command::new("ffprobe")
        .args(&args)
        .arg(path)
        .stdin(Stdio::null())
        .output()?;

    let value: Value = serde_json::from_reader(&out.stdout[..])?;

    let streams = value
        .as_object()
        .and_then(|m| m.get("streams"))
        .and_then(|v| v.as_array())
        .ok_or(Error::Voice(VoiceError::Streams))?;

    let check = streams.iter().any(|stream| {
        let channels = stream.as_object().and_then(|m| {
            m.get("channels").and_then(|v| v.as_i64())
        });

        channels == Some(2)
    });

    Ok(check)
}
<commit_msg>Remove debug logging<commit_after>use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use serde_json;
use std::ffi::OsStr;
use std::io::{BufReader, ErrorKind as IoErrorKind, Read, Result as IoResult};
use std::process::{Child, Command, Stdio};
use super::{AudioSource, AudioType, DcaError, DcaMetadata, VoiceError};
use internal::prelude::*;

use std::fs::File;
use std::result::Result as StdResult;

struct ChildContainer(Child);

impl Read for ChildContainer {
    fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> {
        self.0.stdout.as_mut().unwrap().read(buffer)
    }
}

struct InputSource<R: Read + Send + 'static> {
    stereo: bool,
    reader: R,
    kind: AudioType,
}

impl<R: Read + Send> AudioSource for InputSource<R> {
    fn is_stereo(&mut self) -> bool { self.stereo }

    fn get_type(&self) -> AudioType { self.kind.clone() }

    fn read_pcm_frame(&mut self, buffer: &mut [i16]) -> Option<usize> {
        for (i, v) in buffer.iter_mut().enumerate() {
            *v = match self.reader.read_i16::<LittleEndian>() {
                Ok(v) => v,
                Err(ref e) => {
                    return if e.kind() == IoErrorKind::UnexpectedEof {
                        Some(i)
                    } else {
                        None
                    }
                },
            }
        }

        Some(buffer.len())
    }

    fn read_opus_frame(&mut self) -> Option<Vec<u8>> {
        match self.reader.read_i16::<LittleEndian>() {
            Ok(size) => {
                let mut frame = Vec::with_capacity(size as usize);

                {
                    let reader = self.reader.by_ref();

                    if let Err(_) = reader.take(size as u64).read_to_end(&mut frame) {
                        return None;
                    }
                }

                Some(frame)
            },
            Err(ref e) => {
                return if e.kind() == IoErrorKind::UnexpectedEof {
                    Some(Vec::new())
                } else {
                    None
                }
            },
        }
    }
}

/// Opens an audio file through `ffmpeg` and creates an audio source.
pub fn ffmpeg<P: AsRef<OsStr>>(path: P) -> Result<Box<AudioSource>> {
    let path = path.as_ref();

    // Will fail if the path is not to a file on the fs. Likely a YouTube URI.
    let is_stereo = is_stereo(path).unwrap_or(false);
    let stereo_val = if is_stereo { "2" } else { "1" };

    let args = [
        "-f",
        "s16le",
        "-ac",
        stereo_val,
        "-ar",
        "48000",
        "-acodec",
        "pcm_s16le",
        "-",
    ];

    let command = Command::new("ffmpeg")
        .arg("-i")
        .arg(path)
        .args(&args)
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    Ok(pcm(is_stereo, ChildContainer(command)))
}

/// Creates a streamed audio source from a DCA file.
/// Currently only accepts the DCA1 format.
pub fn dca<P: AsRef<OsStr>>(path: P) -> StdResult<Box<AudioSource>, DcaError> {
    let file = File::open(path.as_ref()).map_err(DcaError::IoError)?;

    let mut reader = BufReader::new(file);

    let mut header = [0u8; 4];

    // Read in the magic number to verify it's a DCA file.
    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    if header != b"DCA1"[..] {
        return Err(DcaError::InvalidHeader);
    }

    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    let size = (&header[..]).read_i32::<LittleEndian>().unwrap();

    // Sanity check
    if size < 2 {
        return Err(DcaError::InvalidSize(size));
    }

    let mut raw_json = Vec::with_capacity(size as usize);

    {
        let json_reader = reader.by_ref();
        json_reader.take(size as u64).read_to_end(&mut raw_json).map_err(DcaError::IoError)?;
    }

    let metadata = serde_json::from_slice::<DcaMetadata>(raw_json.as_slice()).map_err(DcaError::InvalidMetadata)?;

    Ok(opus(metadata.is_stereo(), reader))
}

/// Creates an Opus audio source.
pub fn opus<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader: reader,
        kind: AudioType::Opus
    })
}

/// Creates a PCM audio source.
pub fn pcm<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader: reader,
        kind: AudioType::Pcm
    })
}

/// Creates a streamed audio source with `youtube-dl` and `ffmpeg`.
pub fn ytdl(uri: &str) -> Result<Box<AudioSource>> {
    let args = [
        "-f",
        "webm[abr>0]/bestaudio/best",
        "--no-playlist",
        "--print-json",
        "--skip-download",
        uri,
    ];

    let out = Command::new("youtube-dl")
        .args(&args)
        .stdin(Stdio::null())
        .output()?;

    if !out.status.success() {
        return Err(Error::Voice(VoiceError::YouTubeDLRun(out)));
    }

    let value = serde_json::from_reader(&out.stdout[..])?;
    let mut obj = match value {
        Value::Object(obj) => obj,
        other => return Err(Error::Voice(VoiceError::YouTubeDLProcessing(other))),
    };

    let uri = match obj.remove("url") {
        Some(v) => {
            match v {
                Value::String(uri) => uri,
                other => return Err(Error::Voice(VoiceError::YouTubeDLUrl(other))),
            }
        },
        None => return Err(Error::Voice(VoiceError::YouTubeDLUrl(Value::Object(obj)))),
    };

    ffmpeg(&uri)
}

fn is_stereo(path: &OsStr) -> Result<bool> {
    let args = ["-v", "quiet", "-of", "json", "-show-streams", "-i"];

    let out = Command::new("ffprobe")
        .args(&args)
        .arg(path)
        .stdin(Stdio::null())
        .output()?;

    let value: Value = serde_json::from_reader(&out.stdout[..])?;

    let streams = value
        .as_object()
        .and_then(|m| m.get("streams"))
        .and_then(|v| v.as_array())
        .ok_or(Error::Voice(VoiceError::Streams))?;

    let check = streams.iter().any(|stream| {
        let channels = stream.as_object().and_then(|m| {
            m.get("channels").and_then(|v| v.as_i64())
        });

        channels == Some(2)
    });

    Ok(check)
}
<|endoftext|>


<commit_before>use frame::ChannelAssignment;

fn decode_left_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let left = buffer[i];
    let side = buffer[i + block_size];

    // right channel
    buffer[i + block_size] = left - side;
  }
}

fn decode_right_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let side  = buffer[i];
    let right = buffer[i + block_size];

    // left channel
    buffer[i] = side + right;
  }
}

fn decode_middle_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let mut middle = buffer[i];
    let side       = buffer[i + block_size];

    middle = (middle << 1) | (side & 1);

    // left and right channel
    buffer[i]              = (middle + side) >> 1;
    buffer[i + block_size] = (middle - side) >> 1;
  }
}

pub fn decode(channel_assignment: ChannelAssignment, buffer: &mut [i32]) {
  match channel_assignment {
    ChannelAssignment::Independent => return,
    ChannelAssignment::LeftSide    => decode_left_side(buffer),
    ChannelAssignment::RightSide   => decode_right_side(buffer),
    ChannelAssignment::MiddleSide  => decode_middle_side(buffer),
  }
}
<commit_msg>Make `decode_left_side` public for testing<commit_after>use frame::ChannelAssignment;

pub fn decode_left_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let left = buffer[i];
    let side = buffer[i + block_size];

    // right channel
    buffer[i + block_size] = left - side;
  }
}

fn decode_right_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let side  = buffer[i];
    let right = buffer[i + block_size];

    // left channel
    buffer[i] = side + right;
  }
}

fn decode_middle_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let mut middle = buffer[i];
    let side       = buffer[i + block_size];

    middle = (middle << 1) | (side & 1);

    // left and right channel
    buffer[i]              = (middle + side) >> 1;
    buffer[i + block_size] = (middle - side) >> 1;
  }
}

pub fn decode(channel_assignment: ChannelAssignment, buffer: &mut [i32]) {
  match channel_assignment {
    ChannelAssignment::Independent => return,
    ChannelAssignment::LeftSide    => decode_left_side(buffer),
    ChannelAssignment::RightSide   => decode_right_side(buffer),
    ChannelAssignment::MiddleSide  => decode_middle_side(buffer),
  }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! Frame timings — Object holding timing information for a single frame

use glib::translate::*;
use ffi;

pub struct FrameTimings(*mut ffi::GdkFrameTimings);

impl FrameTimings {
    #[inline]
    fn add_ref(&self) {
        unsafe { ffi::gdk_frame_timings_ref(self.0); }
    }

    #[inline]
    fn unref(&self) {
        unsafe { ffi::gdk_frame_timings_unref(self.0) }
    }

    pub fn get_frame_counter(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_frame_counter(self.0) }
    }

    pub fn get_complete(&self) -> bool {
        unsafe { from_glib(ffi::gdk_frame_timings_get_complete(self.0)) }
    }

    pub fn get_frame_time(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_frame_time(self.0) }
    }

    pub fn get_presentation_time(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_presentation_time(self.0) }
    }

    pub fn get_refresh_interval(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_refresh_interval(self.0) }
    }

    pub fn get_predicted_presentation_time(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_predicted_presentation_time(self.0) }
    }
}

impl Clone for FrameTimings {
    #[inline]
    fn clone(&self) -> FrameTimings {
        self.add_ref();
        FrameTimings(self.0)
    }
}

impl Drop for FrameTimings {
    #[inline]
    fn drop(&mut self) {
        self.unref();
    }
}

impl<'a> ToGlibPtr<'a, *mut ffi::GdkFrameTimings> for &'a FrameTimings {
    type Storage = &'a FrameTimings;

    #[inline]
    fn to_glib_none(&self) -> Stash<'a, *mut ffi::GdkFrameTimings, &'a FrameTimings> {
        Stash(self.0, *self)
    }

    #[inline]
    fn to_glib_full(&self) -> *mut ffi::GdkFrameTimings {
        self.add_ref();
        self.0
    }
}

impl FromGlibPtr<*mut ffi::GdkFrameTimings> for FrameTimings {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::GdkFrameTimings) -> FrameTimings {
        assert!(!ptr.is_null());
        let res = FrameTimings(ptr);
        res.add_ref();
        res
    }

    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::GdkFrameTimings) -> FrameTimings {
        assert!(!ptr.is_null());
        FrameTimings(ptr)
    }
}
<commit_msg>Use glib_wrapper to implement FrameTimings<commit_after>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use glib::translate::*;
use ffi;

glib_wrapper! {
    /// Object holding timing information for a single frame.
    pub struct FrameTimings(Refcounted<ffi::GdkFrameTimings>);

    match fn {
        ref => |ptr| ffi::gdk_frame_timings_ref(ptr),
        unref => |ptr| ffi::gdk_frame_timings_unref(ptr),
    }
}

impl FrameTimings {
    pub fn get_frame_counter(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_frame_counter(self.to_glib_none().0) }
    }

    pub fn get_complete(&self) -> bool {
        unsafe { from_glib(ffi::gdk_frame_timings_get_complete(self.to_glib_none().0)) }
    }

    pub fn get_frame_time(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_frame_time(self.to_glib_none().0) }
    }

    pub fn get_presentation_time(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_presentation_time(self.to_glib_none().0) }
    }

    pub fn get_refresh_interval(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_refresh_interval(self.to_glib_none().0) }
    }

    pub fn get_predicted_presentation_time(&self) -> i64 {
        unsafe { ffi::gdk_frame_timings_get_predicted_presentation_time(self.to_glib_none().0) }
    }
}
<|endoftext|>


<commit_before>use crate::thir::visit::{self, Visitor};

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_middle::thir::*;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
use rustc_session::lint::Level;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::symbol::Symbol;
use rustc_span::Span;

use std::ops::Bound;

struct UnsafetyVisitor<'a, 'tcx> {
    tcx: TyCtxt<'tcx>,
    thir: &'a Thir<'tcx>,
    /// The `HirId` of the current scope, which would be the `HirId`
    /// of the current HIR node, modulo adjustments. Used for lint levels.
    hir_context: hir::HirId,
    /// The current "safety context". This notably tracks whether we are in an
    /// `unsafe` block, and whether it has been used.
    safety_context: SafetyContext,
    body_unsafety: BodyUnsafety,
    /// The `#[target_feature]` attributes of the body. Used for checking
    /// calls to functions with `#[target_feature]` (RFC 2396).
    body_target_features: &'tcx Vec<Symbol>,
    is_const: bool,
}

impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
    fn in_safety_context<R>(
        &mut self,
        safety_context: SafetyContext,
        f: impl FnOnce(&mut Self) -> R,
    ) {
        if let (
            SafetyContext::UnsafeBlock { span: enclosing_span, .. },
            SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
        ) = (self.safety_context, safety_context)
        {
            self.warn_unused_unsafe(
                hir_id,
                block_span,
                Some(self.tcx.sess.source_map().guess_head_span(enclosing_span)),
            );
            f(self);
        } else {
            let prev_context = self.safety_context;
            self.safety_context = safety_context;

            f(self);

            if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
                self.warn_unused_unsafe(hir_id, span, self.body_unsafety.unsafe_fn_sig_span());
            }
            self.safety_context = prev_context;
            return;
        }
    }

    fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
        let (description, note) = kind.description_and_note();
        let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
        match self.safety_context {
            SafetyContext::UnsafeBlock { ref mut used, .. } => {
                if !self.body_unsafety.is_unsafe() || !unsafe_op_in_unsafe_fn_allowed {
                    // Mark this block as useful
                    *used = true;
                }
            }
            SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
            SafetyContext::UnsafeFn => {
                // unsafe_op_in_unsafe_fn is disallowed
                self.tcx.struct_span_lint_hir(
                    UNSAFE_OP_IN_UNSAFE_FN,
                    self.hir_context,
                    span,
                    |lint| {
                        lint.build(&format!(
                            "{} is unsafe and requires unsafe block (error E0133)",
                            description,
                        ))
                        .span_label(span, description)
                        .note(note)
                        .emit();
                    },
                )
            }
            SafetyContext::Safe => {
                let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
                struct_span_err!(
                    self.tcx.sess,
                    span,
                    E0133,
                    "{} is unsafe and requires unsafe{} block",
                    description,
                    fn_sugg,
                )
                .span_label(span, description)
                .note(note)
                .emit();
            }
        }
    }

    fn warn_unused_unsafe(
        &self,
        hir_id: hir::HirId,
        block_span: Span,
        enclosing_span: Option<Span>,
    ) {
        let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
        self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, |lint| {
            let msg = "unnecessary `unsafe` block";
            let mut db = lint.build(msg);
            db.span_label(block_span, msg);
            if let Some(enclosing_span) = enclosing_span {
                db.span_label(
                    enclosing_span,
                    format!("because it's nested under this `unsafe` block"),
                );
            }
            db.emit();
        });
    }

    /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
    fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
    }
}

impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
    fn thir(&self) -> &'a Thir<'tcx> {
        &self.thir
    }

    fn visit_block(&mut self, block: &Block) {
        if let BlockSafety::ExplicitUnsafe(hir_id) = block.safety_mode {
            self.in_safety_context(
                SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
                |this| visit::walk_block(this, block),
            );
        } else {
            visit::walk_block(self, block);
        }
    }

    fn visit_expr(&mut self, expr: &Expr<'tcx>) {
        match expr.kind {
            ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
                let prev_id = self.hir_context;
                self.hir_context = hir_id;
                self.visit_expr(&self.thir[value]);
                self.hir_context = prev_id;
                return;
            }
            ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
                if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
                    self.requires_unsafe(expr.span, CallToUnsafeFunction);
                } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
                    // If the called function has target features the calling function hasn't,
                    // the call requires `unsafe`.
                    if !self
                        .tcx
                        .codegen_fn_attrs(func_did)
                        .target_features
                        .iter()
                        .all(|feature| self.body_target_features.contains(feature))
                    {
                        self.requires_unsafe(expr.span, CallToFunctionWith);
                    }
                }
            }
            ExprKind::Deref { arg } => {
                if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
                    if self.tcx.is_mutable_static(def_id) {
                        self.requires_unsafe(expr.span, UseOfMutableStatic);
                    } else if self.tcx.is_foreign_item(def_id) {
                        self.requires_unsafe(expr.span, UseOfExternStatic);
                    }
                } else if self.thir[arg].ty.is_unsafe_ptr() {
                    self.requires_unsafe(expr.span, DerefOfRawPointer);
                }
            }
            ExprKind::InlineAsm { .. } | ExprKind::LlvmInlineAsm { .. } => {
                self.requires_unsafe(expr.span, UseOfInlineAssembly);
            }
            ExprKind::Adt {
                adt_def,
                variant_index: _,
                substs: _,
                user_ty: _,
                fields: _,
                base: _,
            } => match self.tcx.layout_scalar_valid_range(adt_def.did) {
                (Bound::Unbounded, Bound::Unbounded) => {}
                _ => self.requires_unsafe(expr.span, InitializingTypeWith),
            },
            ExprKind::Cast { source } => {
                let source = &self.thir[source];
                if self.tcx.features().const_raw_ptr_to_usize_cast
                    && self.is_const
                    && (source.ty.is_unsafe_ptr() || source.ty.is_fn_ptr())
                    && expr.ty.is_integral()
                {
                    self.requires_unsafe(expr.span, CastOfPointerToInt);
                }
            }
            _ => {}
        }

        visit::walk_expr(self, expr);
    }
}

#[derive(Clone, Copy)]
enum SafetyContext {
    Safe,
    UnsafeFn,
    UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
}

#[derive(Clone, Copy)]
enum BodyUnsafety {
    /// The body is not unsafe.
    Safe,
    /// The body is an unsafe function. The span points to
    /// the signature of the function.
    Unsafe(Span),
}

impl BodyUnsafety {
    /// Returns whether the body is unsafe.
    fn is_unsafe(&self) -> bool {
        matches!(self, BodyUnsafety::Unsafe(_))
    }

    /// If the body is unsafe, returns the `Span` of its signature.
    fn unsafe_fn_sig_span(self) -> Option<Span> {
        match self {
            BodyUnsafety::Unsafe(span) => Some(span),
            BodyUnsafety::Safe => None,
        }
    }
}

#[derive(Clone, Copy, PartialEq)]
enum UnsafeOpKind {
    CallToUnsafeFunction,
    UseOfInlineAssembly,
    InitializingTypeWith,
    CastOfPointerToInt,
    UseOfMutableStatic,
    UseOfExternStatic,
    DerefOfRawPointer,
    #[allow(dead_code)] // FIXME
    AssignToDroppingUnionField,
    #[allow(dead_code)] // FIXME
    AccessToUnionField,
    #[allow(dead_code)] // FIXME
    MutationOfLayoutConstrainedField,
    #[allow(dead_code)] // FIXME
    BorrowOfLayoutConstrainedField,
    CallToFunctionWith,
}

use UnsafeOpKind::*;

impl UnsafeOpKind {
    pub fn description_and_note(&self) -> (&'static str, &'static str) {
        match self {
            CallToUnsafeFunction => (
                "call to unsafe function",
                "consult the function's documentation for information on how to avoid undefined \
                 behavior",
            ),
            UseOfInlineAssembly => (
                "use of inline assembly",
                "inline assembly is entirely unchecked and can cause undefined behavior",
            ),
            InitializingTypeWith => (
                "initializing type with `rustc_layout_scalar_valid_range` attr",
                "initializing a layout restricted type's field with a value outside the valid \
                 range is undefined behavior",
            ),
            CastOfPointerToInt => {
                ("cast of pointer to int", "casting pointers to integers in constants")
            }
            UseOfMutableStatic => (
                "use of mutable static",
                "mutable statics can be mutated by multiple threads: aliasing violations or data \
                 races will cause undefined behavior",
            ),
            UseOfExternStatic => (
                "use of extern static",
                "extern statics are not controlled by the Rust type system: invalid data, \
                 aliasing violations or data races will cause undefined behavior",
            ),
            DerefOfRawPointer => (
                "dereference of raw pointer",
                "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
                 and cause data races: all of these are undefined behavior",
            ),
            AssignToDroppingUnionField => (
                "assignment to union field that might need dropping",
                "the previous content of the field will be dropped, which causes undefined \
                 behavior if the field was not properly initialized",
            ),
            AccessToUnionField => (
                "access to union field",
                "the field may not be properly initialized: using uninitialized data will cause \
                 undefined behavior",
            ),
            MutationOfLayoutConstrainedField => (
                "mutation of layout constrained field",
                "mutating layout constrained fields cannot statically be checked for valid values",
            ),
            BorrowOfLayoutConstrainedField => (
                "borrow of layout constrained field with interior mutability",
                "references to fields of layout constrained fields lose the constraints. Coupled \
                 with interior mutability, the field can be changed to invalid values",
            ),
            CallToFunctionWith => (
                "call to function with `#[target_feature]`",
                "can only be called if the required target features are available",
            ),
        }
    }
}

// FIXME: checking unsafety for closures should be handled by their parent body,
// as they inherit their "safety context" from their declaration site.
pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
    // THIR unsafeck is gated under `-Z thir-unsafeck`
    if !tcx.sess.opts.debugging_opts.thir_unsafeck {
        return;
    }

    let (thir, expr) = tcx.thir_body(def);
    let thir = &thir.borrow();
    // If `thir` is empty, a type error occured, skip this body.
    if thir.exprs.is_empty() {
        return;
    }

    let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
    let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
        if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
            BodyUnsafety::Unsafe(fn_sig.span)
        } else {
            BodyUnsafety::Safe
        }
    });
    let body_target_features = &tcx.codegen_fn_attrs(def.did).target_features;
    let safety_context =
        if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
    let is_const = match tcx.hir().body_owner_kind(hir_id) {
        hir::BodyOwnerKind::Closure => false,
        hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def.did.to_def_id()),
        hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true,
    };
    let mut visitor = UnsafetyVisitor {
        tcx,
        thir,
        safety_context,
        hir_context: hir_id,
        body_unsafety,
        body_target_features,
        is_const,
    };
    visitor.visit_expr(&thir[expr]);
}

crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
    if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
        tcx.thir_check_unsafety_for_const_arg(def)
    } else {
        check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
    }
}

crate fn thir_check_unsafety_for_const_arg<'tcx>(
    tcx: TyCtxt<'tcx>,
    (did, param_did): (LocalDefId, DefId),
) {
    check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
}
<commit_msg>Fix `unused_unsafe` in THIR unsafeck<commit_after>use crate::thir::visit::{self, Visitor};

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_middle::thir::*;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
use rustc_session::lint::Level;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::symbol::Symbol;
use rustc_span::Span;

use std::ops::Bound;

struct UnsafetyVisitor<'a, 'tcx> {
    tcx: TyCtxt<'tcx>,
    thir: &'a Thir<'tcx>,
    /// The `HirId` of the current scope, which would be the `HirId`
    /// of the current HIR node, modulo adjustments. Used for lint levels.
    hir_context: hir::HirId,
    /// The current "safety context". This notably tracks whether we are in an
    /// `unsafe` block, and whether it has been used.
    safety_context: SafetyContext,
    body_unsafety: BodyUnsafety,
    /// The `#[target_feature]` attributes of the body. Used for checking
    /// calls to functions with `#[target_feature]` (RFC 2396).
    body_target_features: &'tcx Vec<Symbol>,
    is_const: bool,
}

impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
    fn in_safety_context<R>(
        &mut self,
        safety_context: SafetyContext,
        f: impl FnOnce(&mut Self) -> R,
    ) {
        if let (
            SafetyContext::UnsafeBlock { span: enclosing_span, .. },
            SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
        ) = (self.safety_context, safety_context)
        {
            self.warn_unused_unsafe(
                hir_id,
                block_span,
                Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")),
            );
            f(self);
        } else {
            let prev_context = self.safety_context;
            self.safety_context = safety_context;

            f(self);

            if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
                self.warn_unused_unsafe(
                    hir_id,
                    span,
                    if self.unsafe_op_in_unsafe_fn_allowed() {
                        self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn"))
                    } else {
                        None
                    },
                );
            }
            self.safety_context = prev_context;
            return;
        }
    }

    fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
        let (description, note) = kind.description_and_note();
        let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
        match self.safety_context {
            SafetyContext::UnsafeBlock { ref mut used, .. } => {
                if !self.body_unsafety.is_unsafe() || !unsafe_op_in_unsafe_fn_allowed {
                    // Mark this block as useful
                    *used = true;
                }
            }
            SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
            SafetyContext::UnsafeFn => {
                // unsafe_op_in_unsafe_fn is disallowed
                self.tcx.struct_span_lint_hir(
                    UNSAFE_OP_IN_UNSAFE_FN,
                    self.hir_context,
                    span,
                    |lint| {
                        lint.build(&format!(
                            "{} is unsafe and requires unsafe block (error E0133)",
                            description,
                        ))
                        .span_label(span, description)
                        .note(note)
                        .emit();
                    },
                )
            }
            SafetyContext::Safe => {
                let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
                struct_span_err!(
                    self.tcx.sess,
                    span,
                    E0133,
                    "{} is unsafe and requires unsafe{} block",
                    description,
                    fn_sugg,
                )
                .span_label(span, description)
                .note(note)
                .emit();
            }
        }
    }

    fn warn_unused_unsafe(
        &self,
        hir_id: hir::HirId,
        block_span: Span,
        enclosing_unsafe: Option<(Span, &'static str)>,
    ) {
        let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
        self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, |lint| {
            let msg = "unnecessary `unsafe` block";
            let mut db = lint.build(msg);
            db.span_label(block_span, msg);
            if let Some((span, kind)) = enclosing_unsafe {
                db.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
            }
            db.emit();
        });
    }

    /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
    fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
    }
}

impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
    fn thir(&self) -> &'a Thir<'tcx> {
        &self.thir
    }

    fn visit_block(&mut self, block: &Block) {
        if let BlockSafety::ExplicitUnsafe(hir_id) = block.safety_mode {
            self.in_safety_context(
                SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
                |this| visit::walk_block(this, block),
            );
        } else {
            visit::walk_block(self, block);
        }
    }

    fn visit_expr(&mut self, expr: &Expr<'tcx>) {
        match expr.kind {
            ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
                let prev_id = self.hir_context;
                self.hir_context = hir_id;
                self.visit_expr(&self.thir[value]);
                self.hir_context = prev_id;
                return;
            }
            ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
                if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
                    self.requires_unsafe(expr.span, CallToUnsafeFunction);
                } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
                    // If the called function has target features the calling function hasn't,
                    // the call requires `unsafe`.
                    if !self
                        .tcx
                        .codegen_fn_attrs(func_did)
                        .target_features
                        .iter()
                        .all(|feature| self.body_target_features.contains(feature))
                    {
                        self.requires_unsafe(expr.span, CallToFunctionWith);
                    }
                }
            }
            ExprKind::Deref { arg } => {
                if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
                    if self.tcx.is_mutable_static(def_id) {
                        self.requires_unsafe(expr.span, UseOfMutableStatic);
                    } else if self.tcx.is_foreign_item(def_id) {
                        self.requires_unsafe(expr.span, UseOfExternStatic);
                    }
                } else if self.thir[arg].ty.is_unsafe_ptr() {
                    self.requires_unsafe(expr.span, DerefOfRawPointer);
                }
            }
            ExprKind::InlineAsm { .. } | ExprKind::LlvmInlineAsm { .. } => {
                self.requires_unsafe(expr.span, UseOfInlineAssembly);
            }
            ExprKind::Adt {
                adt_def,
                variant_index: _,
                substs: _,
                user_ty: _,
                fields: _,
                base: _,
            } => match self.tcx.layout_scalar_valid_range(adt_def.did) {
                (Bound::Unbounded, Bound::Unbounded) => {}
                _ => self.requires_unsafe(expr.span, InitializingTypeWith),
            },
            ExprKind::Cast { source } => {
                let source = &self.thir[source];
                if self.tcx.features().const_raw_ptr_to_usize_cast
                    && self.is_const
                    && (source.ty.is_unsafe_ptr() || source.ty.is_fn_ptr())
                    && expr.ty.is_integral()
                {
                    self.requires_unsafe(expr.span, CastOfPointerToInt);
                }
            }
            _ => {}
        }

        visit::walk_expr(self, expr);
    }
}

#[derive(Clone, Copy)]
enum SafetyContext {
    Safe,
    UnsafeFn,
    UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
}

#[derive(Clone, Copy)]
enum BodyUnsafety {
    /// The body is not unsafe.
    Safe,
    /// The body is an unsafe function. The span points to
    /// the signature of the function.
    Unsafe(Span),
}

impl BodyUnsafety {
    /// Returns whether the body is unsafe.
    fn is_unsafe(&self) -> bool {
        matches!(self, BodyUnsafety::Unsafe(_))
    }

    /// If the body is unsafe, returns the `Span` of its signature.
    fn unsafe_fn_sig_span(self) -> Option<Span> {
        match self {
            BodyUnsafety::Unsafe(span) => Some(span),
            BodyUnsafety::Safe => None,
        }
    }
}

#[derive(Clone, Copy, PartialEq)]
enum UnsafeOpKind {
    CallToUnsafeFunction,
    UseOfInlineAssembly,
    InitializingTypeWith,
    CastOfPointerToInt,
    UseOfMutableStatic,
    UseOfExternStatic,
    DerefOfRawPointer,
    #[allow(dead_code)] // FIXME
    AssignToDroppingUnionField,
    #[allow(dead_code)] // FIXME
    AccessToUnionField,
    #[allow(dead_code)] // FIXME
    MutationOfLayoutConstrainedField,
    #[allow(dead_code)] // FIXME
    BorrowOfLayoutConstrainedField,
    CallToFunctionWith,
}

use UnsafeOpKind::*;

impl UnsafeOpKind {
    pub fn description_and_note(&self) -> (&'static str, &'static str) {
        match self {
            CallToUnsafeFunction => (
                "call to unsafe function",
                "consult the function's documentation for information on how to avoid undefined \
                 behavior",
            ),
            UseOfInlineAssembly => (
                "use of inline assembly",
                "inline assembly is entirely unchecked and can cause undefined behavior",
            ),
            InitializingTypeWith => (
                "initializing type with `rustc_layout_scalar_valid_range` attr",
                "initializing a layout restricted type's field with a value outside the valid \
                 range is undefined behavior",
            ),
            CastOfPointerToInt => {
                ("cast of pointer to int", "casting pointers to integers in constants")
            }
            UseOfMutableStatic => (
                "use of mutable static",
                "mutable statics can be mutated by multiple threads: aliasing violations or data \
                 races will cause undefined behavior",
            ),
            UseOfExternStatic => (
                "use of extern static",
                "extern statics are not controlled by the Rust type system: invalid data, \
                 aliasing violations or data races will cause undefined behavior",
            ),
            DerefOfRawPointer => (
                "dereference of raw pointer",
                "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
                 and cause data races: all of these are undefined behavior",
            ),
            AssignToDroppingUnionField => (
                "assignment to union field that might need dropping",
                "the previous content of the field will be dropped, which causes undefined \
                 behavior if the field was not properly initialized",
            ),
            AccessToUnionField => (
                "access to union field",
                "the field may not be properly initialized: using uninitialized data will cause \
                 undefined behavior",
            ),
            MutationOfLayoutConstrainedField => (
                "mutation of layout constrained field",
                "mutating layout constrained fields cannot statically be checked for valid values",
            ),
            BorrowOfLayoutConstrainedField => (
                "borrow of layout constrained field with interior mutability",
                "references to fields of layout constrained fields lose the constraints. Coupled \
                 with interior mutability, the field can be changed to invalid values",
            ),
            CallToFunctionWith => (
                "call to function with `#[target_feature]`",
                "can only be called if the required target features are available",
            ),
        }
    }
}

// FIXME: checking unsafety for closures should be handled by their parent body,
// as they inherit their "safety context" from their declaration site.
pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
    // THIR unsafeck is gated under `-Z thir-unsafeck`
    if !tcx.sess.opts.debugging_opts.thir_unsafeck {
        return;
    }

    let (thir, expr) = tcx.thir_body(def);
    let thir = &thir.borrow();
    // If `thir` is empty, a type error occured, skip this body.
    if thir.exprs.is_empty() {
        return;
    }

    let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
    let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
        if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
            BodyUnsafety::Unsafe(fn_sig.span)
        } else {
            BodyUnsafety::Safe
        }
    });
    let body_target_features = &tcx.codegen_fn_attrs(def.did).target_features;
    let safety_context =
        if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
    let is_const = match tcx.hir().body_owner_kind(hir_id) {
        hir::BodyOwnerKind::Closure => false,
        hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def.did.to_def_id()),
        hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true,
    };
    let mut visitor = UnsafetyVisitor {
        tcx,
        thir,
        safety_context,
        hir_context: hir_id,
        body_unsafety,
        body_target_features,
        is_const,
    };
    visitor.visit_expr(&thir[expr]);
}

crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
    if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
        tcx.thir_check_unsafety_for_const_arg(def)
    } else {
        check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
    }
}

crate fn thir_check_unsafety_for_const_arg<'tcx>(
    tcx: TyCtxt<'tcx>,
    (did, param_did): (LocalDefId, DefId),
) {
    check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
}
<|endoftext|>