<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Wire format traits.
//!
//! This module provides [`FromWire`] and [`ToWire`], a pair of traits similar
//! to the core traits in the [`serde`] library. Rather than representing a
//! generically serializeable type, they represent types that can be converted
//! to and from Cerberus's wire format, which has a unique, ad-hoc data model.
//!
//! [`FromWire`]: trait.FromWire.html
//! [`ToWire`]: trait.ToWire.html
//! [`serde`]: https://serde.rs

use core::fmt;

use crate::io;
use crate::io::endian::LeInt;
use crate::io::Read;
use crate::io::Write;
use crate::mem::Arena;
use crate::mem::OutOfMemory;

/// A type which can be deserialized from the Cerberus wire format.
///
/// The lifetime `'wire` indicates that the type can be deserialized from a
/// buffer of lifetime `'wire`.
pub trait FromWire<'wire>: Sized {
    /// Deserializes a `Self` w of `r`.
    fn from_wire<R: Read, A: Arena>(
        r: R,
        arena: &'wire A,
    ) -> Result<Self, FromWireError>;
}

/// An deserialization error.
#[derive(Clone, Copy, Debug)]
pub enum FromWireError {
    /// Indicates that something went wrong in an `io` operation.
    ///
    /// [`io`]: ../../io/index.html
    Io(io::Error),

    /// Indicates that the arena used to allocate dynamic portions of the
    /// deserialization ran out of memory.
    OutOfMemory,

    /// Indicates that some field within the request was outside of its
    /// valid range.
    OutOfRange,
}

impl From<io::Error> for FromWireError {
    fn from(e: io::Error) -> Self {
        Self::Io(e)
    }
}

impl From<OutOfMemory> for FromWireError {
    fn from(_: OutOfMemory) -> Self {
        Self::OutOfMemory
    }
}

/// A type which can be serialized into the Cerberus wire format.
pub trait ToWire: Sized {
    /// Serializes `self` into `w`.
    fn to_wire<W: Write>(&self, w: W) -> Result<(), ToWireError>;
}

/// A serializerion error.
#[derive(Clone, Copy, Debug)]
pub enum ToWireError {
    /// Indicates that something went wrong in an [`io`] operation.
    ///
    /// [`io`]: ../../io/index.html
    Io(io::Error),
}

impl From<io::Error> for ToWireError {
    fn from(e: io::Error) -> Self {
        Self::Io(e)
    }
}

/// Represents a C-like enum that can be converted to and from a wire
/// representation as well as to and from a string representation.
///
/// An implementation of this trait can be thought of as an unsigned
/// integer with a limited range: every enum variant can be converted
/// to the wire format and back, though not every value of the wire
/// representation can be converted into an enum variant.
///
/// In particular the following identity must hold for all types T:
/// ```
/// # use manticore::protocol::wire::WireEnum;
/// # fn test<T: WireEnum + Copy + PartialEq + std::fmt::Debug>(x: T) {
/// assert_eq!(T::from_wire_value(T::to_wire_value(x)), Some(x));
/// # }
/// ```
///
/// Also, the following identity must hold for all types T:
/// ```
/// # use manticore::protocol::wire::WireEnum;
/// # fn test<T: WireEnum + Copy + PartialEq + std::fmt::Debug>(x: T) {
/// assert_eq!(T::from_name(T::name(x)), Some(x));
/// # }
/// ```
pub trait WireEnum: Sized + Copy {
    /// The unrelying "wire type". This is almost always some kind of
    /// unsigned integer.
    type Wire: LeInt;

    /// Converts `self` into its underlying wire representation.
    fn to_wire_value(self) -> Self::Wire;

    /// Attempts to parse a value of `Self` from the underlying wire
    /// representation.
    fn from_wire_value(wire: Self::Wire) -> Option<Self>;

    /// Converts `self` into a string representation.
    fn name(self) -> &'static str;

    /// Attempts to convert a value of `Self` from a string representation.
    fn from_name(str: &str) -> Option<Self>;
}

impl<'wire, E> FromWire<'wire> for E
where
    E: WireEnum,
{
    fn from_wire<R: Read, A: Arena>(
        mut r: R,
        _: &'wire A,
    ) -> Result<Self, FromWireError> {
        let wire = <Self as WireEnum>::Wire::read_from(&mut r)?;
        Self::from_wire_value(wire).ok_or(FromWireError::OutOfRange)
    }
}

impl<E> ToWire for E
where
    E: WireEnum,
{
    fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
        self.to_wire_value().write_to(&mut w)?;
        Ok(())
    }
}

/// A deserialization-from-string error.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct WireEnumFromStrError;

impl fmt::Display for WireEnumFromStrError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "unknown variant")
    }
}

/// A conveinence macro for generating `WireEnum`-implementing enums.
///
///
/// Syntax is as follows:
/// ```text
/// wire_enum! {
///     /// This is my enum.
///     pub enum MyEnum : u8 {
///         /// Variant `A`.
///         A = 0x00,
///         /// Variant `B`.
///         B = 0x01,
///     }
/// }
/// ```
/// This macro will generate an implementation of `WireEnum<Wire=u8>` for
/// the above enum.
macro_rules! wire_enum {
    ($(#[$meta:meta])* $vis:vis enum $name:ident : $wire:ident {
        $($(#[$meta_variant:meta])* $variant:ident = $value:literal,)*
    }) => {
        $(#[$meta])*
        #[repr($wire)]
        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
        $vis enum $name {
           $(
               $(#[$meta_variant])*
               $variant = $value,
           )*
        }

        impl $crate::protocol::wire::WireEnum for $name {
            type Wire = $wire;
            fn to_wire_value(self) -> Self::Wire {
                self as $wire
            }
            fn from_wire_value(wire: Self::Wire) -> Option<Self> {
                match wire {
                    $(
                        $value => Some(Self::$variant),
                    )*
                    _ => None,
                }
            }

            fn name(self) -> &'static str {
                match self {
                    $(
                        Self::$variant => stringify!($variant),
                    )*
                }
            }

            fn from_name(name: &str) -> Option<Self> {
                match name {
                    $(
                        stringify!($variant) => Some(Self::$variant),
                    )*
                    _ => None,
                }
            }
        }

        impl core::fmt::Display for $name {
            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
                use $crate::protocol::wire::WireEnum;

                write!(f, "{}", self.name())
            }
        }

        impl core::str::FromStr for $name {
            type Err = $crate::protocol::wire::WireEnumFromStrError;

            fn from_str(s: &str) -> Result<Self, $crate::protocol::wire::WireEnumFromStrError> {
                use $crate::protocol::wire::WireEnum;

                match $name::from_name(s) {
                    Some(val) => Ok(val),
                    None => Err($crate::protocol::wire::WireEnumFromStrError),
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    wire_enum! {
        /// An enum for testing.
        pub enum DemoEnum: u8 {
            /// Unknown value
            Unknown = 0x00,

            /// First enum value
            First = 0x01,

            /// Second enum value
            Second = 0x02,
        }
    }

    #[test]
    fn from_name() {
        use crate::protocol::wire::*;

        let value = DemoEnum::from_name("Second").expect("from_name failed");
        assert_eq!(value, DemoEnum::Second);

        let value = DemoEnum::from_name("First").expect("from_name failed");
        assert_eq!(value, DemoEnum::First);

        assert_eq!(None, DemoEnum::from_name("does not exist"));
    }

    #[test]
    fn name() {
        use crate::protocol::wire::*;

        assert_eq!(DemoEnum::First.name(), "First");
        assert_eq!(DemoEnum::Second.name(), "Second");
    }
}
<commit_msg>[protocol] Don't require that WireEnum::Wire: LeInt<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Wire format traits.
//!
//! This module provides [`FromWire`] and [`ToWire`], a pair of traits similar
//! to the core traits in the [`serde`] library. Rather than representing a
//! generically serializeable type, they represent types that can be converted
//! to and from Cerberus's wire format, which has a unique, ad-hoc data model.
//!
//! [`FromWire`]: trait.FromWire.html
//! [`ToWire`]: trait.ToWire.html
//! [`serde`]: https://serde.rs

use core::fmt;

use crate::io;
use crate::io::endian::LeInt;
use crate::io::Read;
use crate::io::Write;
use crate::mem::Arena;
use crate::mem::OutOfMemory;

/// A type which can be deserialized from the Cerberus wire format.
///
/// The lifetime `'wire` indicates that the type can be deserialized from a
/// buffer of lifetime `'wire`.
pub trait FromWire<'wire>: Sized {
    /// Deserializes a `Self` w of `r`.
    fn from_wire<R: Read, A: Arena>(
        r: R,
        arena: &'wire A,
    ) -> Result<Self, FromWireError>;
}

/// An deserialization error.
#[derive(Clone, Copy, Debug)]
pub enum FromWireError {
    /// Indicates that something went wrong in an `io` operation.
    ///
    /// [`io`]: ../../io/index.html
    Io(io::Error),

    /// Indicates that the arena used to allocate dynamic portions of the
    /// deserialization ran out of memory.
    OutOfMemory,

    /// Indicates that some field within the request was outside of its
    /// valid range.
    OutOfRange,
}

impl From<io::Error> for FromWireError {
    fn from(e: io::Error) -> Self {
        Self::Io(e)
    }
}

impl From<OutOfMemory> for FromWireError {
    fn from(_: OutOfMemory) -> Self {
        Self::OutOfMemory
    }
}

/// A type which can be serialized into the Cerberus wire format.
pub trait ToWire: Sized {
    /// Serializes `self` into `w`.
    fn to_wire<W: Write>(&self, w: W) -> Result<(), ToWireError>;
}

/// A serializerion error.
#[derive(Clone, Copy, Debug)]
pub enum ToWireError {
    /// Indicates that something went wrong in an [`io`] operation.
    ///
    /// [`io`]: ../../io/index.html
    Io(io::Error),
}

impl From<io::Error> for ToWireError {
    fn from(e: io::Error) -> Self {
        Self::Io(e)
    }
}

/// Represents a C-like enum that can be converted to and from a wire
/// representation as well as to and from a string representation.
///
/// An implementation of this trait can be thought of as an unsigned
/// integer with a limited range: every enum variant can be converted
/// to the wire format and back, though not every value of the wire
/// representation can be converted into an enum variant.
///
/// In particular the following identity must hold for all types T:
/// ```
/// # use manticore::protocol::wire::WireEnum;
/// # fn test<T: WireEnum + Copy + PartialEq + std::fmt::Debug>(x: T) {
/// assert_eq!(T::from_wire_value(T::to_wire_value(x)), Some(x));
/// # }
/// ```
///
/// Also, the following identity must hold for all types T:
/// ```
/// # use manticore::protocol::wire::WireEnum;
/// # fn test<T: WireEnum + Copy + PartialEq + std::fmt::Debug>(x: T) {
/// assert_eq!(T::from_name(T::name(x)), Some(x));
/// # }
/// ```
pub trait WireEnum: Sized + Copy {
    /// The unrelying "wire type". This is almost always some kind of
    /// unsigned integer.
    type Wire;

    /// Converts `self` into its underlying wire representation.
    fn to_wire_value(self) -> Self::Wire;

    /// Attempts to parse a value of `Self` from the underlying wire
    /// representation.
    fn from_wire_value(wire: Self::Wire) -> Option<Self>;

    /// Converts `self` into a string representation.
    fn name(self) -> &'static str;

    /// Attempts to convert a value of `Self` from a string representation.
    fn from_name(str: &str) -> Option<Self>;
}

impl<'wire, E> FromWire<'wire> for E
where
    E: WireEnum,
    E::Wire: LeInt,
{
    fn from_wire<R: Read, A: Arena>(
        mut r: R,
        _: &'wire A,
    ) -> Result<Self, FromWireError> {
        let wire = <Self as WireEnum>::Wire::read_from(&mut r)?;
        Self::from_wire_value(wire).ok_or(FromWireError::OutOfRange)
    }
}

impl<E> ToWire for E
where
    E: WireEnum,
    E::Wire: LeInt,
{
    fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
        self.to_wire_value().write_to(&mut w)?;
        Ok(())
    }
}

/// A deserialization-from-string error.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct WireEnumFromStrError;

impl fmt::Display for WireEnumFromStrError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "unknown variant")
    }
}

/// A conveinence macro for generating `WireEnum`-implementing enums.
///
///
/// Syntax is as follows:
/// ```text
/// wire_enum! {
///     /// This is my enum.
///     pub enum MyEnum : u8 {
///         /// Variant `A`.
///         A = 0x00,
///         /// Variant `B`.
///         B = 0x01,
///     }
/// }
/// ```
/// This macro will generate an implementation of `WireEnum<Wire=u8>` for
/// the above enum.
macro_rules! wire_enum {
    ($(#[$meta:meta])* $vis:vis enum $name:ident : $wire:ident {
        $($(#[$meta_variant:meta])* $variant:ident = $value:tt,)*
    }) => {
        $(#[$meta])*
        #[repr($wire)]
        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
        $vis enum $name {
           $(
               $(#[$meta_variant])*
               $variant = $value,
           )*
        }

        impl $crate::protocol::wire::WireEnum for $name {
            type Wire = $wire;
            fn to_wire_value(self) -> Self::Wire {
                match self {
                    $(
                        Self::$variant => $value,
                    )*
                }
            }
            fn from_wire_value(wire: Self::Wire) -> Option<Self> {
                match wire {
                    $(
                        $value => Some(Self::$variant),
                    )*
                    _ => None,
                }
            }

            fn name(self) -> &'static str {
                match self {
                    $(
                        Self::$variant => stringify!($variant),
                    )*
                }
            }

            fn from_name(name: &str) -> Option<Self> {
                match name {
                    $(
                        stringify!($variant) => Some(Self::$variant),
                    )*
                    _ => None,
                }
            }
        }

        impl core::fmt::Display for $name {
            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
                use $crate::protocol::wire::WireEnum;

                write!(f, "{}", self.name())
            }
        }

        impl core::str::FromStr for $name {
            type Err = $crate::protocol::wire::WireEnumFromStrError;

            fn from_str(s: &str) -> Result<Self, $crate::protocol::wire::WireEnumFromStrError> {
                use $crate::protocol::wire::WireEnum;

                match $name::from_name(s) {
                    Some(val) => Ok(val),
                    None => Err($crate::protocol::wire::WireEnumFromStrError),
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    wire_enum! {
        /// An enum for testing.
        pub enum DemoEnum: u8 {
            /// Unknown value
            Unknown = 0x00,

            /// First enum value
            First = 0x01,

            /// Second enum value
            Second = 0x02,
        }
    }

    #[test]
    fn from_name() {
        use crate::protocol::wire::*;

        let value = DemoEnum::from_name("Second").expect("from_name failed");
        assert_eq!(value, DemoEnum::Second);

        let value = DemoEnum::from_name("First").expect("from_name failed");
        assert_eq!(value, DemoEnum::First);

        assert_eq!(None, DemoEnum::from_name("does not exist"));
    }

    #[test]
    fn name() {
        use crate::protocol::wire::*;

        assert_eq!(DemoEnum::First.name(), "First");
        assert_eq!(DemoEnum::Second.name(), "Second");
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use byteorder::{ByteOrder, LittleEndian};
use protobuf::RepeatedField;
use murmur3::murmur3_x64_128;
use tipb::analyze;

/// `CMSketch` is used to estimate point queries.
/// Refer:[Count-Min Sketch](https://en.wikipedia.org/wiki/Count-min_sketch)
#[derive(Clone)]
pub struct CMSketch {
    depth: usize,
    width: usize,
    count: u32,
    table: Vec<Vec<u32>>,
}

impl CMSketch {
    pub fn new(d: usize, w: usize) -> Option<CMSketch> {
        if d == 0 || w == 0 {
            None
        } else {
            Some(CMSketch {
                depth: d,
                width: w,
                count: 0,
                table: vec![vec![0; w]; d],
            })
        }
    }

    // `hash` hashes the data into two u64 using murmur hash.
    fn hash(mut bytes: &[u8]) -> (u64, u64) {
        let mut out: [u8; 16] = [0; 16];
        murmur3_x64_128(&mut bytes, 0, &mut out);
        (
            LittleEndian::read_u64(&out[0..8]),
            LittleEndian::read_u64(&out[8..16]),
        )
    }

    // `insert` inserts the data into cm sketch. For each row i, the position at
    // (h1 + h2*i) % width will be incremented by one, where the (h1, h2) is the hash value
    // of data.
    pub fn insert(&mut self, bytes: &[u8]) {
        self.count = self.count.wrapping_add(1);
        let (h1, h2) = CMSketch::hash(bytes);
        for (i, row) in self.table.iter_mut().enumerate() {
            let j = (h1.wrapping_add(h2.wrapping_mul(i as u64)) % self.width as u64) as usize;
            row[j] = row[j].saturating_add(1);
        }
    }

    pub fn into_proto(self) -> analyze::CMSketch {
        let mut proto = analyze::CMSketch::new();
        let mut rows = vec![analyze::CMSketchRow::default(); self.depth];
        for (i, row) in self.table.iter().enumerate() {
            rows[i].set_counters(row.to_vec());
        }
        proto.set_rows(RepeatedField::from_vec(rows));
        proto
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::cmp::min;
    use rand::{Rng, SeedableRng, StdRng};
    use zipf::ZipfDistribution;
    use coprocessor::codec::datum;
    use coprocessor::codec::datum::Datum;
    use util::as_slice;
    use super::*;

    impl CMSketch {
        fn query(&self, bytes: &[u8]) -> u32 {
            let (h1, h2) = CMSketch::hash(bytes);
            let mut vals = vec![0u32; self.depth];
            let mut min_counter = u32::max_value();
            for (i, row) in self.table.iter().enumerate() {
                let j = (h1.wrapping_add(h2.wrapping_mul(i as u64)) % self.width as u64) as usize;
                let noise = (self.count - row[j]) / (self.width as u32 - 1);
                vals[i] = row[j].saturating_sub(noise);
                min_counter = min(min_counter, row[j])
            }
            vals.sort();
            min(
                min_counter,
                vals[(self.depth - 1) / 2] +
                    (vals[self.depth / 2] - vals[(self.depth - 1) / 2]) / 2,
            )
        }

        pub fn count(&self) -> u32 {
            self.count
        }
    }

    fn average_error(depth: usize, width: usize, total: u32, max_value: usize, s: f64) -> u64 {
        let mut c = CMSketch::new(depth, width).unwrap();
        let mut map: HashMap<u64, u32> = HashMap::new();
        let seed: &[_] = &[1, 2, 3, 4];
        let mut gen: ZipfDistribution<StdRng> =
            ZipfDistribution::new(SeedableRng::from_seed(seed), max_value, s).unwrap();
        for _ in 0..total {
            let val = gen.next_u64();
            let bytes = datum::encode_value(as_slice(&Datum::U64(val))).unwrap();
            c.insert(&bytes);
            let counter = map.entry(val).or_insert(0);
            *counter += 1;
        }
        let mut total = 0u64;
        for (val, num) in &map {
            let bytes = datum::encode_value(as_slice(&Datum::U64(*val))).unwrap();
            let estimate = c.query(&bytes);
            let err = if *num > estimate {
                *num - estimate
            } else {
                estimate - *num
            };
            total += err as u64
        }
        total / map.len() as u64
    }

    #[test]
    fn test_cm_sketch() {
        let (depth, width) = (8, 2048);
        let (total, max_value) = (1000000, 10000000);
        assert_eq!(average_error(depth, width, total, max_value, 1.1), 3);
        assert_eq!(average_error(depth, width, total, max_value, 2.0), 24);
        assert_eq!(average_error(depth, width, total, max_value, 3.0), 64);
    }
}
<commit_msg>coprocessor: make test CM sketch faster (#2577)<commit_after>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use byteorder::{ByteOrder, LittleEndian};
use protobuf::RepeatedField;
use murmur3::murmur3_x64_128;
use tipb::analyze;

/// `CMSketch` is used to estimate point queries.
/// Refer:[Count-Min Sketch](https://en.wikipedia.org/wiki/Count-min_sketch)
#[derive(Clone)]
pub struct CMSketch {
    depth: usize,
    width: usize,
    count: u32,
    table: Vec<Vec<u32>>,
}

impl CMSketch {
    pub fn new(d: usize, w: usize) -> Option<CMSketch> {
        if d == 0 || w == 0 {
            None
        } else {
            Some(CMSketch {
                depth: d,
                width: w,
                count: 0,
                table: vec![vec![0; w]; d],
            })
        }
    }

    // `hash` hashes the data into two u64 using murmur hash.
    fn hash(mut bytes: &[u8]) -> (u64, u64) {
        let mut out: [u8; 16] = [0; 16];
        murmur3_x64_128(&mut bytes, 0, &mut out);
        (
            LittleEndian::read_u64(&out[0..8]),
            LittleEndian::read_u64(&out[8..16]),
        )
    }

    // `insert` inserts the data into cm sketch. For each row i, the position at
    // (h1 + h2*i) % width will be incremented by one, where the (h1, h2) is the hash value
    // of data.
    pub fn insert(&mut self, bytes: &[u8]) {
        self.count = self.count.wrapping_add(1);
        let (h1, h2) = CMSketch::hash(bytes);
        for (i, row) in self.table.iter_mut().enumerate() {
            let j = (h1.wrapping_add(h2.wrapping_mul(i as u64)) % self.width as u64) as usize;
            row[j] = row[j].saturating_add(1);
        }
    }

    pub fn into_proto(self) -> analyze::CMSketch {
        let mut proto = analyze::CMSketch::new();
        let mut rows = vec![analyze::CMSketchRow::default(); self.depth];
        for (i, row) in self.table.iter().enumerate() {
            rows[i].set_counters(row.to_vec());
        }
        proto.set_rows(RepeatedField::from_vec(rows));
        proto
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::cmp::min;
    use rand::{Rng, SeedableRng, StdRng};
    use zipf::ZipfDistribution;
    use coprocessor::codec::datum;
    use coprocessor::codec::datum::Datum;
    use util::as_slice;
    use super::*;

    impl CMSketch {
        fn query(&self, bytes: &[u8]) -> u32 {
            let (h1, h2) = CMSketch::hash(bytes);
            let mut vals = vec![0u32; self.depth];
            let mut min_counter = u32::max_value();
            for (i, row) in self.table.iter().enumerate() {
                let j = (h1.wrapping_add(h2.wrapping_mul(i as u64)) % self.width as u64) as usize;
                let noise = (self.count - row[j]) / (self.width as u32 - 1);
                vals[i] = row[j].saturating_sub(noise);
                min_counter = min(min_counter, row[j])
            }
            vals.sort();
            min(
                min_counter,
                vals[(self.depth - 1) / 2] +
                    (vals[self.depth / 2] - vals[(self.depth - 1) / 2]) / 2,
            )
        }

        pub fn count(&self) -> u32 {
            self.count
        }
    }

    fn average_error(depth: usize, width: usize, total: u32, max_value: usize, s: f64) -> u64 {
        let mut c = CMSketch::new(depth, width).unwrap();
        let mut map: HashMap<u64, u32> = HashMap::new();
        let seed: &[_] = &[1, 2, 3, 4];
        let mut gen: ZipfDistribution<StdRng> =
            ZipfDistribution::new(SeedableRng::from_seed(seed), max_value, s).unwrap();
        for _ in 0..total {
            let val = gen.next_u64();
            let bytes = datum::encode_value(as_slice(&Datum::U64(val))).unwrap();
            c.insert(&bytes);
            let counter = map.entry(val).or_insert(0);
            *counter += 1;
        }
        let mut total = 0u64;
        for (val, num) in &map {
            let bytes = datum::encode_value(as_slice(&Datum::U64(*val))).unwrap();
            let estimate = c.query(&bytes);
            let err = if *num > estimate {
                *num - estimate
            } else {
                estimate - *num
            };
            total += err as u64
        }
        total / map.len() as u64
    }

    #[test]
    fn test_cm_sketch() {
        let (depth, width) = (8, 2048);
        let (total, max_value) = (10000, 10000000);
        assert_eq!(average_error(depth, width, total, max_value, 1.1), 1);
        assert_eq!(average_error(depth, width, total, max_value, 2.0), 2);
        assert_eq!(average_error(depth, width, total, max_value, 3.0), 2);
    }
}
<|endoftext|>


<commit_before>use core::mem::transmute;
use core::option::Some;

use kernel::heap;
use kernel::memory::Allocator;
use kernel::memory::physical::{alloc_frames, zero_alloc_frames};
use kernel::int;
use kernel::rt::memset;
use kernel;
use cpu::idt;
use cpu::interrupt::IsrStack;
use platform::io;

pub static PRESENT: u32 = 1 << 0;
pub static RW:      u32 = 1 << 1;
pub static USER:    u32 = 1 << 2;

static CR0_PG: u32 = 1 << 31;

static PAGE_SIZE: u32 = 0x1000;
static ENTRIES:   u32 = 1024;

static DIRECTORY_VADDR: u32 = 0xFFFFF000;
static directory: *mut PageDirectory = DIRECTORY_VADDR as *mut PageDirectory;

#[packed]
struct Page(u32);

#[packed]
struct PageTable {
    pages: [Page, ..1024]
}

// // U: underlying element type
// #[packed]
// struct Table<U> {
//     pages: [Page, ..1024]
// }

// type PageTable = Table<Page>;
// type PageDirectory = Table<Table<Page>>;

#[packed]
pub struct PageDirectory {
    tables: [Page, ..1024]
}

pub unsafe fn init() {
    let dir = zero_alloc_frames(1) as *mut PageDirectory;
    let table = alloc_frames(1) as *mut PageTable;

    (*table).identity_map(0, PRESENT | RW);
    // (*dir).tables[0] = Page::new(table as u32, PRESENT | RW);
    (*dir).set(transmute(0), table, PRESENT | RW);

    // Map the directory as its own last table.
    // (phys *directory)Dir last table => (phys *table)Dir, last page => virt *last_page
    // When accessing its virtual address
    // (*dir).tables[DIRECTORY_VADDR >> 22] = Page::new(dir as u32, PRESENT | RW);
    (*dir).set(directory, dir, PRESENT | RW);

    kernel::int_table.map(|t| {
        use cpu::interrupt::{Isr, IsrStack, Fault};
        use cpu::exception::{PAGE_FAULT, exception_handler};
        (*t.table)[PAGE_FAULT as u8] = Isr::new(Fault(PAGE_FAULT), true).idt_entry(exception_handler());
    });

    (*dir).switch();
}

pub unsafe fn map(page_ptr: *mut u8, flags: u32) {
    /*let vaddr = page_ptr as u32;
    let phys_ptr = alloc_frames(1);

    let table = alloc_frames(1) as *mut PageTable;

    (*table).pages[(vaddr >> 12) & 0x3ff] = Page(phys_ptr as u32 | PRESENT | RW | USER);
    kernel::page_dir.map(|p| {
        // TODO: set page; directory vaddr
        if !(*directory).tables[vaddr >> 22].present() {

        }
        (*directory).tables[vaddr >> 22] = Page::new(table as u32, PRESENT | RW);
    });*/
    // io::puti(page_ptr as int);
    let table = (*directory).fetch_table(page_ptr as u32, flags | PRESENT);
    (*table).set(page_ptr, alloc_frames(1), flags | PRESENT);
    flush_tlb(page_ptr);
}

fn flush_tlb<T>(addr: T) {
    unsafe {
        asm!("invlpg ($0)" :: "r"(addr) : "volatile", "memory");
    }
}

impl PageDirectory {
    fn fetch_table(&mut self, addr: u32, flags: u32) -> *mut PageTable {
        let index = addr / (PAGE_SIZE * ENTRIES);
        if self.tables[index].present() {
            match self.tables[index] {
                Page(v) => (v & !(0b111 as u32)) as *mut PageTable
            }
        }
        else {
            // allocate table
            unsafe {
                let table = zero_alloc_frames(1) as *mut PageTable;
                (*directory).set(addr as *mut PageTable, table, flags);
                table
            }
        }
    }

    unsafe fn set<T>(&mut self, addr: *mut T, entry: *mut T, flags: u32) {
        let index = addr as u32 / (PAGE_SIZE * ENTRIES);
        self.tables[index] = Page(entry as u32 | flags);
    }

    unsafe fn get<T>(&mut self, addr: *mut T) -> u32 {
        let index = addr as u32 / (PAGE_SIZE * ENTRIES);
        match self.tables[index] { Page(entry) => entry }
    }

    pub unsafe fn switch(&self) {
        asm!("mov cr3, $0

              mov eax, cr0
              or eax, $1
              mov cr0, eax"
            :: "{eax}"(self), "n"(CR0_PG)
            :: "intel")
    }
}

impl Page {
    fn new(addr: u32, flags: u32) -> Page {
        Page(addr | flags)
    }

    fn at_frame(i: uint, flags: u32) -> Page {
        Page::new((i as u32) * PAGE_SIZE, flags)
    }

    fn present(self) -> bool {
        match self {
            Page(v) => (v & PRESENT) != 0
        }
    }
}

impl PageTable {
    fn identity_map(&mut self, start: uint, flags: u32) {
        // TODO: use dmemset. zero_alloc_frames use sse.
        int::range(0, 1024, |i| {
            self.pages[i] = Page::at_frame(start + i, flags);
        });
    }

    unsafe fn set<T>(&mut self, addr: *mut T, entry: *mut T, flags: u32) {
        let index = (addr as u32 / PAGE_SIZE) % ENTRIES;
        self.pages[index] = Page(entry as u32 | flags);
    }
}
<commit_msg>x86: MMU Table type parameter.<commit_after>use core::mem::{transmute, size_of};

use kernel::memory::physical::{alloc_frames, zero_alloc_frames};
use kernel::int;
use kernel;

pub static PRESENT: u32 = 1 << 0;
pub static RW:      u32 = 1 << 1;
pub static USER:    u32 = 1 << 2;

static CR0_PG: u32 = 1 << 31;

static PAGE_SIZE: uint = 0x1000;
static ENTRIES:   uint = 1024;

static DIRECTORY_VADDR: u32 = 0xFFFFF000;
static directory: *mut PageDirectory = DIRECTORY_VADDR as *mut PageDirectory;

#[packed]
struct Page(u32);

// U: underlying element type
#[packed]
struct Table<U> {
    entries: [Page, ..1024]
}

pub type PageTable = Table<Page>;
pub type PageDirectory = Table<Table<Page>>;

pub unsafe fn init() {
    let dir = zero_alloc_frames(1) as *mut PageDirectory;
    let table = alloc_frames(1) as *mut PageTable;

    (*table).identity_map(0, PRESENT | RW);
    (*dir).set(transmute(0), table, PRESENT | RW);

    // Map the directory as its own last table.
    // When accessing its virtual address
    (*dir).set(directory, dir, PRESENT | RW);

    kernel::int_table.map(|t| {
        use cpu::interrupt::{Isr, Fault};
        use cpu::exception::{PAGE_FAULT, exception_handler};
        (*t.table)[PAGE_FAULT as u8] = Isr::new(Fault(PAGE_FAULT), true).idt_entry(exception_handler());
        // exception_info[14] = info;
    });

    (*dir).switch();
}

pub unsafe fn map(page_ptr: *mut u8, flags: u32) {
    let table = (*directory).fetch_table(page_ptr as u32, flags | PRESENT);
    (*table).set(page_ptr, alloc_frames(1), flags | PRESENT);
    flush_tlb(page_ptr);
}

fn flush_tlb<T>(addr: T) {
    unsafe {
        asm!("invlpg [$0]" :: "r"(addr) : "memory" : "volatile", "intel")
    }
}

impl Page {
    fn new(addr: u32, flags: u32) -> Page {
        Page(addr | flags)
    }

    fn at_frame(i: uint, flags: u32) -> Page {
        Page::new((i * PAGE_SIZE) as u32, flags)
    }

    fn present(self) -> bool {
        match self {
            Page(v) => (v & PRESENT) != 0
        }
    }
}

impl<U> Table<U> {
    fn set<T>(&mut self, addr: *mut T, entry: *mut T, flags: u32) {
        // update entry, based on the underlying type (page, table)
        let len = size_of::<U>() / size_of::<Page>();
        let index = (addr as uint / PAGE_SIZE / len) % ENTRIES;
        self.entries[index] = Page::new(entry as u32, flags);
    }
}

impl Table<Page> {
    fn identity_map(&mut self, start: uint, flags: u32) {
        // TODO: use dmemset. zero_alloc_frames: use sse.
        int::range(0, 1024, |i| {
            self.entries[i] = Page::at_frame(start + i, flags);
        });
    }
}

// Can't impl on typedefs. Rust #9767
impl Table<Table<Page>> {
    fn fetch_table(&mut self, addr: u32, flags: u32) -> *mut PageTable {
        let index = addr as uint / (PAGE_SIZE * ENTRIES);
        let table = self.entries[index];
        match self.entries[index] {
            Page(p) if table.present() => {
                (p & 0xFFFFF000) as *mut PageTable
            }
            _ => unsafe { // allocate table
                let table = zero_alloc_frames(1) as *mut PageTable;
                (*directory).set(addr as *mut PageTable, table, flags);
                table
            }
        }
    }

    unsafe fn switch(&self) {
        asm!("mov cr3, $0

              mov eax, cr0
              or eax, $1
              mov cr0, eax"
            :: "{eax}"(self), "n"(CR0_PG)
            :: "intel")
    }
}
<|endoftext|>


<commit_before><commit_msg>stop testing RTSP paths on the first RTSP 200 or RTSP 401<commit_after><|endoftext|>


<commit_before>use ram::schema::{Schema, Field};
use ram::cell::*;
use ram::types;
use ram::types::{u16_io, u8_io, Value};

fn read_field(ptr: usize, field: &Field) -> (DataValue, usize) {
    let mut ptr = ptr;
    if field.nullable {
        let null_byte = u8_io::read(ptr);
        ptr += 1;
        if null_byte == 1 {
            return (Value::Null, ptr);
        }
    }
    if field.is_array {
        let len = u16_io::read(ptr);
        let mut sub_field = field.clone();
        sub_field.is_array = false;
        ptr += u16_io::size(ptr);
        let mut val = Vec::<DataValue>::new();
        for _ in 0..len {
            let (nxt_val, nxt_ptr) = read_field(ptr, &sub_field);
            ptr = nxt_ptr;
            val.push(nxt_val);
        }
        (Value::Array(val), ptr)
    } else if let Some(ref subs) = field.sub_fields {
        let mut map = DataMap::new();
        for sub in subs {
            let (cval, cptr) = read_field(ptr, &sub);
            map.insert_key_id(sub.name_id, cval);
            ptr = cptr;
        }
        map.fields = subs.iter().map(|sub| &sub.name).cloned().collect();
        (Value::Map(map), ptr)
    } else {
        (types::get_val(field.type_id, ptr), ptr + types::get_size(field.type_id, ptr))
    }
}

pub fn read_by_schema(ptr: usize, schema: &Schema) -> DataValue {
    let (val, _) = read_field(ptr, &schema.fields);
    val
}<commit_msg>rename for read array<commit_after>use ram::schema::{Schema, Field};
use ram::cell::*;
use ram::types;
use ram::types::{u16_io, u8_io, Value};

fn read_field(ptr: usize, field: &Field) -> (DataValue, usize) {
    let mut ptr = ptr;
    if field.nullable {
        let null_byte = u8_io::read(ptr);
        ptr += 1;
        if null_byte == 1 {
            return (Value::Null, ptr);
        }
    }
    if field.is_array {
        let len = u16_io::read(ptr);
        let mut sub_field = field.clone();
        sub_field.is_array = false;
        ptr += u16_io::size(ptr);
        let mut vals = Vec::<DataValue>::new();
        for _ in 0..len {
            let (nxt_val, nxt_ptr) = read_field(ptr, &sub_field);
            ptr = nxt_ptr;
            vals.push(nxt_val);
        }
        (Value::Array(vals), ptr)
    } else if let Some(ref subs) = field.sub_fields {
        let mut map = DataMap::new();
        for sub in subs {
            let (cval, cptr) = read_field(ptr, &sub);
            map.insert_key_id(sub.name_id, cval);
            ptr = cptr;
        }
        map.fields = subs.iter().map(|sub| &sub.name).cloned().collect();
        (Value::Map(map), ptr)
    } else {
        (types::get_val(field.type_id, ptr), ptr + types::get_size(field.type_id, ptr))
    }
}

pub fn read_by_schema(ptr: usize, schema: &Schema) -> DataValue {
    let (val, _) = read_field(ptr, &schema.fields);
    val
}<|endoftext|>


<commit_before>use std::ffi::CStr;
use std::ptr;
use libc::c_int;
use super::ffi;

// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
#[derive(Debug)]
pub struct RawStatement(*mut ffi::sqlite3_stmt);

impl RawStatement {
    pub fn new(stmt: *mut ffi::sqlite3_stmt) -> RawStatement {
        RawStatement(stmt)
    }

    pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
        self.0
    }

    pub fn column_count(&self) -> c_int {
        unsafe { ffi::sqlite3_column_count(self.0) }
    }

    pub fn column_name(&self, idx: c_int) -> &CStr {
        unsafe { CStr::from_ptr(ffi::sqlite3_column_name(self.0, idx)) }
    }

    pub fn step(&self) -> c_int {
        unsafe { ffi::sqlite3_step(self.0) }
    }

    pub fn reset(&self) -> c_int {
        unsafe { ffi::sqlite3_reset(self.0) }
    }

    pub fn bind_parameter_count(&self) -> c_int {
        unsafe { ffi::sqlite3_bind_parameter_count(self.0) }
    }

    pub fn bind_parameter_index(&self, name: &CStr) -> Option<c_int> {
        let r = unsafe { ffi::sqlite3_bind_parameter_index(self.0, name.as_ptr()) };
        match r {
            0 => None,
            i => Some(i),
        }
    }

    pub fn clear_bindings(&self) -> c_int {
        unsafe { ffi::sqlite3_clear_bindings(self.0) }
    }

    pub fn sql(&self) -> &CStr {
        unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.0)) }
    }

    pub fn finalize(mut self) -> c_int {
        self.finalize_()
    }

    fn finalize_(&mut self) -> c_int {
        let r = unsafe { ffi::sqlite3_finalize(self.0) };
        self.0 = ptr::null_mut();
        r
    }
}

impl Drop for RawStatement {
    fn drop(&mut self) {
        self.finalize_();
    }
}
<commit_msg>Add RawStatement::column_type.<commit_after>use std::ffi::CStr;
use std::ptr;
use libc::c_int;
use super::ffi;

// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
#[derive(Debug)]
pub struct RawStatement(*mut ffi::sqlite3_stmt);

impl RawStatement {
    pub fn new(stmt: *mut ffi::sqlite3_stmt) -> RawStatement {
        RawStatement(stmt)
    }

    pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
        self.0
    }

    pub fn column_count(&self) -> c_int {
        unsafe { ffi::sqlite3_column_count(self.0) }
    }

    pub fn column_type(&self, idx: c_int) -> c_int {
        unsafe { ffi::sqlite3_column_type(self.0, idx) }
    }

    pub fn column_name(&self, idx: c_int) -> &CStr {
        unsafe { CStr::from_ptr(ffi::sqlite3_column_name(self.0, idx)) }
    }

    pub fn step(&self) -> c_int {
        unsafe { ffi::sqlite3_step(self.0) }
    }

    pub fn reset(&self) -> c_int {
        unsafe { ffi::sqlite3_reset(self.0) }
    }

    pub fn bind_parameter_count(&self) -> c_int {
        unsafe { ffi::sqlite3_bind_parameter_count(self.0) }
    }

    pub fn bind_parameter_index(&self, name: &CStr) -> Option<c_int> {
        let r = unsafe { ffi::sqlite3_bind_parameter_index(self.0, name.as_ptr()) };
        match r {
            0 => None,
            i => Some(i),
        }
    }

    pub fn clear_bindings(&self) -> c_int {
        unsafe { ffi::sqlite3_clear_bindings(self.0) }
    }

    pub fn sql(&self) -> &CStr {
        unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.0)) }
    }

    pub fn finalize(mut self) -> c_int {
        self.finalize_()
    }

    fn finalize_(&mut self) -> c_int {
        let r = unsafe { ffi::sqlite3_finalize(self.0) };
        self.0 = ptr::null_mut();
        r
    }
}

impl Drop for RawStatement {
    fn drop(&mut self) {
        self.finalize_();
    }
}
<|endoftext|>


<commit_before><commit_msg>update node allocation<commit_after><|endoftext|>


<commit_before><commit_msg>Set DEBUG to false for release build<commit_after><|endoftext|>


<commit_before><commit_msg>Fix panic on data slice accesses<commit_after><|endoftext|>


<commit_before><commit_msg>Update documentation.<commit_after><|endoftext|>


<commit_before><commit_msg>Add Arg and its related APIs.<commit_after><|endoftext|>


<commit_before>use crate::context;
use crate::context::memory::Grant;
use crate::memory::{free_frames, used_frames, PAGE_SIZE};
use crate::paging::VirtualAddress;
use crate::paging::entry::EntryFlags;
use crate::syscall::data::{Map, Map2, StatVfs};
use crate::syscall::error::*;
use crate::syscall::flag::{MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE};
use crate::syscall::scheme::Scheme;

pub struct MemoryScheme;

impl MemoryScheme {
    pub fn new() -> Self {
        MemoryScheme
    }
}
impl Scheme for MemoryScheme {
    fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
        Ok(0)
    }

    fn fstatvfs(&self, _file: usize, stat: &mut StatVfs) -> Result<usize> {
        let used = used_frames() as u64;
        let free = free_frames() as u64;

        stat.f_bsize = PAGE_SIZE as u32;
        stat.f_blocks = used + free;
        stat.f_bfree = free;
        stat.f_bavail = stat.f_bfree;

        Ok(0)
    }

    fn fmap2(&self, _id: usize, map: &Map2) -> Result<usize> {
        //TODO: Abstract with other grant creation
        if map.size == 0 {
            Ok(0)
        } else {
            let contexts = context::contexts();
            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
            let context = context_lock.read();

            let fixed = map.flags.contains(MapFlags::MAP_FIXED);
            let fixed_noreplace = map.flags.contains(MapFlags::MAP_FIXED_NOREPLACE);

            let mut grants = context.grants.lock();

            let full_size = ((map.size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;

            let mut to_address = if map.address == 0 { crate::USER_GRANT_OFFSET } else {
                if // map.address < crate::USER_GRANT_OFFSET || map.address + map.size > crate::USER_GRANT_OFFSET + crate::PML4_SIZE ||
                    map.address % PAGE_SIZE != 0
                {
                    return Err(Error::new(EINVAL));
                }
                map.address
            };

            let mut entry_flags = EntryFlags::PRESENT | EntryFlags::USER_ACCESSIBLE;
            if !map.flags.contains(PROT_EXEC) {
                entry_flags |= EntryFlags::NO_EXECUTE;
            }
            if map.flags.contains(PROT_READ) {
                //TODO: PROT_READ
            }
            if map.flags.contains(PROT_WRITE) {
                entry_flags |= EntryFlags::WRITABLE;
            }

            let mut i = 0;

            while i < grants.len()  {
                let grant = &mut grants[i];

                let grant_start = grant.start_address().get();
                let grant_len = ((grant.size() + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
                let grant_end = grant_start + grant_len;

                if to_address < grant_start || grant_end <= to_address {
                    // grant has nothing to do with the memory to map, and thus we can safely just
                    // go on to the next one.

                    if grant_start >= crate::USER_GRANT_OFFSET && !fixed {
                        // don't ignore addresses outside of the automatic grant offset
                        to_address = grant_end;
                    }
                    i += 1;

                    continue;
                }

                // check whether this grant overlaps with the memory range to use, by checking that
                // the start and end of the grant is not within the memory range to map
                if grant_start <= to_address && grant_end > to_address || grant_start <= to_address + full_size && grant_end > to_address + full_size {
                    // the range overlaps, thus we'll have to continue to the next grant, or to
                    // insert a new grant at the end (if not MapFlags::MAP_FIXED).

                    if fixed_noreplace {
                        println!("grant: conflicts with: {:#x} - {:#x}", grant_start, grant_end);
                        return Err(Error::new(EEXIST));
                    } else if fixed {
                        /*
                        // shrink the grant, removing it if necessary. since the to_address isn't
                        // changed at all when mapping to a fixed address, we can just continue to
                        // the next grant and shrink or remove that one if it was also overlapping.
                        if to_address + full_size > grant_start {
                            let new_start = core::cmp::min(grant_end, to_address + full_size);

                            let new_size = grant.size() - (new_start - grant_start);
                            unsafe { grant.set_size(new_size) };
                            grant_len = ((new_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;

                            let new_start = VirtualAddress::new(new_start);
                            unsafe { grant.set_start_address(new_start) };
                            grant_start = new_start;

                            grant_end = grant_start + grant_len;
                        }
                        */
                        // TODO
                        return Err(Error::new(EOPNOTSUPP));
                    } else {
                        to_address = grant_end;
                        i += 1;
                    }
                    continue;
                }

                i += 1;
            }

            grants.insert(i, Grant::map(
                VirtualAddress::new(to_address),
                full_size,
                entry_flags,
            ));

            Ok(to_address)
        }
    }
    fn fmap(&self, id: usize, map: &Map) -> Result<usize> {
        if map.flags.contains(MapFlags::MAP_FIXED) {
            // not supported for fmap, which lacks the address argument.
            return Err(Error::new(EINVAL));
        }
        self.fmap2(id, &Map2 {
            offset: map.offset,
            size: map.size,
            flags: map.flags,
            address: 0,
        })
    }

    fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
        Ok(0)
    }

    fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;
        let scheme_path = b"memory:";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }
        Ok(i)
    }

    fn close(&self, _id: usize) -> Result<usize> {
        Ok(0)
    }
}
<commit_msg>Add restrictions on fmap<commit_after>use core::cmp;
use crate::context;
use crate::context::memory::Grant;
use crate::memory::{free_frames, used_frames, PAGE_SIZE};
use crate::paging::{ActivePageTable, Page, VirtualAddress};
use crate::paging::entry::EntryFlags;
use crate::syscall::data::{Map, Map2, StatVfs};
use crate::syscall::error::*;
use crate::syscall::flag::{MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE};
use crate::syscall::scheme::Scheme;

pub struct MemoryScheme;

impl MemoryScheme {
    pub fn new() -> Self {
        MemoryScheme
    }
}
impl Scheme for MemoryScheme {
    fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
        Ok(0)
    }

    fn fstatvfs(&self, _file: usize, stat: &mut StatVfs) -> Result<usize> {
        let used = used_frames() as u64;
        let free = free_frames() as u64;

        stat.f_bsize = PAGE_SIZE as u32;
        stat.f_blocks = used + free;
        stat.f_bfree = free;
        stat.f_bavail = stat.f_bfree;

        Ok(0)
    }

    fn fmap2(&self, _id: usize, map: &Map2) -> Result<usize> {
        //TODO: Abstract with other grant creation
        if map.size == 0 {
            Ok(0)
        } else {
            let contexts = context::contexts();
            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
            let context = context_lock.read();

            let fixed = map.flags.contains(MapFlags::MAP_FIXED);
            let fixed_noreplace = map.flags.contains(MapFlags::MAP_FIXED_NOREPLACE);

            let mut grants = context.grants.lock();

            let full_size = ((map.size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;

            let mut to_address = if map.address == 0 { crate::USER_GRANT_OFFSET } else {
                if
                    map.address + full_size >= crate::PML4_SIZE * 256 // There are 256 PML4 entries reserved for userspace
                    && map.address % PAGE_SIZE != 0
                {
                    return Err(Error::new(EINVAL));
                }
                map.address
            };

            let mut entry_flags = EntryFlags::PRESENT | EntryFlags::USER_ACCESSIBLE;
            if !map.flags.contains(PROT_EXEC) {
                entry_flags |= EntryFlags::NO_EXECUTE;
            }
            if map.flags.contains(PROT_READ) {
                //TODO: PROT_READ
            }
            if map.flags.contains(PROT_WRITE) {
                entry_flags |= EntryFlags::WRITABLE;
            }

            let mut i = 0;

            while i < grants.len()  {
                let grant = &mut grants[i];

                let grant_start = grant.start_address().get();
                let grant_len = ((grant.size() + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
                let grant_end = grant_start + grant_len;

                if to_address < grant_start || grant_end <= to_address {
                    // grant has nothing to do with the memory to map, and thus we can safely just
                    // go on to the next one.

                    if !fixed {
                        // Use the default grant offset, or if we've already passed it, anything after that.
                        to_address = cmp::max(
                            cmp::max(crate::USER_GRANT_OFFSET, grant_end),
                            to_address,
                        );
                    }
                    i += 1;

                    continue;
                }

                // check whether this grant overlaps with the memory range to use, by checking that
                // the start and end of the grant is not within the memory range to map
                if grant_start <= to_address && grant_end > to_address || grant_start <= to_address + full_size && grant_end > to_address + full_size {
                    // the range overlaps, thus we'll have to continue to the next grant, or to
                    // insert a new grant at the end (if not MapFlags::MAP_FIXED).

                    if fixed_noreplace {
                        println!("grant: conflicts with: {:#x} - {:#x}", grant_start, grant_end);
                        return Err(Error::new(EEXIST));
                    } else if fixed {
                        /*
                        // shrink the grant, removing it if necessary. since the to_address isn't
                        // changed at all when mapping to a fixed address, we can just continue to
                        // the next grant and shrink or remove that one if it was also overlapping.
                        if to_address + full_size > grant_start {
                            let new_start = core::cmp::min(grant_end, to_address + full_size);

                            let new_size = grant.size() - (new_start - grant_start);
                            unsafe { grant.set_size(new_size) };
                            grant_len = ((new_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;

                            let new_start = VirtualAddress::new(new_start);
                            unsafe { grant.set_start_address(new_start) };
                            grant_start = new_start;

                            grant_end = grant_start + grant_len;
                        }
                        */
                        // TODO
                        return Err(Error::new(EOPNOTSUPP));
                    } else {
                        to_address = grant_end;
                        i += 1;
                    }
                    continue;
                }
            }

            let start_address = VirtualAddress::new(to_address);
            let end_address = VirtualAddress::new(to_address + full_size);

            // Make sure it's absolutely not mapped already
            let active_table = unsafe { ActivePageTable::new() };

            for page in Page::range_inclusive(Page::containing_address(start_address), Page::containing_address(end_address)) {
                if active_table.translate_page(page).is_some() {
                    return Err(Error::new(EEXIST))
                }
            }

            grants.insert(i, Grant::map(start_address, full_size, entry_flags));

            Ok(to_address)
        }
    }
    fn fmap(&self, id: usize, map: &Map) -> Result<usize> {
        if map.flags.contains(MapFlags::MAP_FIXED) {
            // not supported for fmap, which lacks the address argument.
            return Err(Error::new(EINVAL));
        }
        self.fmap2(id, &Map2 {
            offset: map.offset,
            size: map.size,
            flags: map.flags,
            address: 0,
        })
    }

    fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
        Ok(0)
    }

    fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;
        let scheme_path = b"memory:";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }
        Ok(i)
    }

    fn close(&self, _id: usize) -> Result<usize> {
        Ok(0)
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use {success, expect, Matcher, MatchResult};

#[derive(Copy)]
pub enum PathType {
    AnyType,
    File,
    Dir
}

#[derive(Copy)]
pub struct ExistingPath {
    path_type: PathType
}

impl ExistingPath {
    fn match_path_type(&self, actual: &Path) -> MatchResult {
        match self.path_type {
            PathType::File => expect(actual.is_file(), format!("`{}` was not a file", actual.display())),
            PathType::Dir => expect(actual.is_dir(), format!("`{}` was not a dir", actual.display())),
            _ => success(),
        }
    }
}

impl fmt::Display for ExistingPath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "an existing file")
    }
}

impl<'a> Matcher<&'a PathBuf> for ExistingPath {
    fn matches(&self, actual: &'a PathBuf) -> MatchResult {
        self.matches(&**actual)
    }
}

impl<'a> Matcher<&'a Path> for ExistingPath {
    fn matches(&self, actual: &Path) -> MatchResult {
        expect(actual.exists(), format!("{} was missing", actual.display()))
            .and(self.match_path_type(actual))
    }
}

pub fn existing_path() -> ExistingPath {
    ExistingPath { path_type: PathType::AnyType }
}

pub fn existing_file() -> ExistingPath {
    ExistingPath { path_type: PathType::File }
}

pub fn existing_dir() -> ExistingPath {
    ExistingPath { path_type: PathType::Dir }
}

#[cfg(test)]
mod test {
    use std::os;
    use {assert_that,is,is_not,existing_file,existing_dir,existing_path};

    #[test]
    fn test_with_existing_file() {
        let path = path(os::getenv("TEST_EXISTS_FILE"), "./README.md");

        assert_that(&path, is(existing_path()));
        assert_that(&path, is(existing_file()));
        assert_that(&path, is_not(existing_dir()));
    }

    #[test]
    fn test_with_existing_dir() {
        let path = path(os::getenv("TEST_EXISTS_DIR"), "./target");

        assert_that(&path, is(existing_path()));
        assert_that(&path, is(existing_dir()));
        assert_that(&path, is_not(existing_file()));
    }

    #[test]
    fn test_with_nonexisting_path() {
        let path = path(os::getenv("TEST_EXISTS_NONE"), "./zomg.txt");

        assert_that(&path, is_not(existing_path()));
        assert_that(&path, is_not(existing_file()));
        assert_that(&path, is_not(existing_dir()));
    }

    fn path(path: Option<String>, default: &str) -> Path {
        Path::new(path.unwrap_or(default.to_string()))
    }
}
<commit_msg>Fix broken Path tests.<commit_after>use std::fmt;
use std::path::{Path, PathBuf};
use std::fs::PathExt;

use {success, expect, Matcher, MatchResult};

#[derive(Copy)]
pub enum PathType {
    AnyType,
    File,
    Dir
}

#[derive(Copy)]
pub struct ExistingPath {
    path_type: PathType
}

impl ExistingPath {
    fn match_path_type(&self, actual: &Path) -> MatchResult {
        match self.path_type {
            PathType::File => expect(actual.is_file(), format!("`{}` was not a file", actual.display())),
            PathType::Dir => expect(actual.is_dir(), format!("`{}` was not a dir", actual.display())),
            _ => success(),
        }
    }
}

impl fmt::Display for ExistingPath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "an existing file")
    }
}

impl<'a> Matcher<&'a PathBuf> for ExistingPath {
    fn matches(&self, actual: &'a PathBuf) -> MatchResult {
        self.matches(&**actual)
    }
}

impl<'a> Matcher<&'a Path> for ExistingPath {
    fn matches(&self, actual: &Path) -> MatchResult {
        expect(actual.exists(), format!("{} was missing", actual.display()))
            .and(self.match_path_type(actual))
    }
}

pub fn existing_path() -> ExistingPath {
    ExistingPath { path_type: PathType::AnyType }
}

pub fn existing_file() -> ExistingPath {
    ExistingPath { path_type: PathType::File }
}

pub fn existing_dir() -> ExistingPath {
    ExistingPath { path_type: PathType::Dir }
}

#[cfg(test)]
mod test {
    use std::os;
    use std::borrow::ToOwned;
    use std::path::{Path, PathBuf};
    use {assert_that, is, is_not, existing_file, existing_dir, existing_path};

    #[test]
    fn test_with_existing_file() {
        let path = path(os::getenv("TEST_EXISTS_FILE"), "./README.md");

        assert_that(&path, is(existing_path()));
        assert_that(&path, is(existing_file()));
        assert_that(&path, is_not(existing_dir()));
    }

    #[test]
    fn test_with_existing_dir() {
        let path = path(os::getenv("TEST_EXISTS_DIR"), "./target");

        assert_that(&path, is(existing_path()));
        assert_that(&path, is(existing_dir()));
        assert_that(&path, is_not(existing_file()));
    }

    #[test]
    fn test_with_nonexisting_path() {
        let path = path(os::getenv("TEST_EXISTS_NONE"), "./zomg.txt");

        assert_that(&path, is_not(existing_path()));
        assert_that(&path, is_not(existing_file()));
        assert_that(&path, is_not(existing_dir()));
    }

    fn path(path: Option<String>, default: &str) -> PathBuf {
        Path::new(&path.unwrap_or(default.to_string())).to_owned()
    }
}
<|endoftext|>


<commit_before>//! This module specifies the type based interner for constants.
//!
//! After a const evaluation has computed a value, before we destroy the const evaluator's session
//! memory, we need to extract all memory allocations to the global memory pool so they stay around.

use rustc::ty::{Ty, TyCtxt, ParamEnv, self};
use rustc::mir::interpret::{
    InterpResult, ErrorHandled, UnsupportedOpInfo,
};
use rustc::hir;
use rustc::hir::def_id::DefId;
use super::validity::RefTracking;
use rustc_data_structures::fx::FxHashSet;

use syntax::ast::Mutability;
use syntax_pos::Span;

use super::{
    ValueVisitor, MemoryKind, Pointer, AllocId, MPlaceTy, InterpError, Scalar,
};
use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};

struct InternVisitor<'rt, 'mir, 'tcx> {
    /// previously encountered safe references
    ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
    ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
    param_env: ParamEnv<'tcx>,
    /// The root node of the value that we're looking at. This field is never mutated and only used
    /// for sanity assertions that will ICE when `const_qualif` screws up.
    mode: InternMode,
    /// This field stores the mutability of the value *currently* being checked.
    /// It is set to mutable when an `UnsafeCell` is encountered
    /// When recursing across a reference, we don't recurse but store the
    /// value to be checked in `ref_tracking` together with the mutability at which we are checking
    /// the value.
    /// When encountering an immutable reference, we treat everything as immutable that is behind
    /// it.
    mutability: Mutability,
    /// A list of all encountered relocations. After type-based interning, we traverse this list to
    /// also intern allocations that are only referenced by a raw pointer or inside a union.
    leftover_relocations: &'rt mut FxHashSet<AllocId>,
}

#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
enum InternMode {
    /// Mutable references must in fact be immutable due to their surrounding immutability in a
    /// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
    /// that will actually be treated as mutable.
    Static,
    /// UnsafeCell is OK in the value of a constant, but not behind references in a constant
    ConstBase,
    /// `UnsafeCell` ICEs
    Const,
}

/// Signalling data structure to ensure we don't recurse
/// into the memory of other constants or statics
struct IsStaticOrFn;

impl<'rt, 'mir, 'tcx> InternVisitor<'rt, 'mir, 'tcx> {
    /// Intern an allocation without looking at its children
    fn intern_shallow(
        &mut self,
        ptr: Pointer,
        mutability: Mutability,
    ) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
        trace!(
            "InternVisitor::intern {:?} with {:?}",
            ptr, mutability,
        );
        // remove allocation
        let tcx = self.ecx.tcx;
        let memory = self.ecx.memory_mut();
        let (kind, mut alloc) = match memory.alloc_map.remove(&ptr.alloc_id) {
            Some(entry) => entry,
            None => {
                // if the pointer is dangling (neither in local nor global memory), we leave it
                // to validation to error. The `delay_span_bug` ensures that we don't forget such
                // a check in validation.
                if tcx.alloc_map.lock().get(ptr.alloc_id).is_none() {
                    tcx.sess.delay_span_bug(self.ecx.tcx.span, "tried to intern dangling pointer");
                }
                // treat dangling pointers like other statics
                // just to stop trying to recurse into them
                return Ok(Some(IsStaticOrFn));
            },
        };
        // This match is just a canary for future changes to `MemoryKind`, which most likely need
        // changes in this function.
        match kind {
            MemoryKind::Stack | MemoryKind::Vtable => {},
        }
        // Ensure llvm knows to only put this into immutable memory if the value is immutable either
        // by being behind a reference or by being part of a static or const without interior
        // mutability
        alloc.mutability = mutability;
        // link the alloc id to the actual allocation
        let alloc = tcx.intern_const_alloc(alloc);
        self.leftover_relocations.extend(alloc.relocations.iter().map(|&(_, ((), reloc))| reloc));
        tcx.alloc_map.lock().set_alloc_id_memory(ptr.alloc_id, alloc);
        Ok(None)
    }
}

impl<'rt, 'mir, 'tcx>
    ValueVisitor<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>
for
    InternVisitor<'rt, 'mir, 'tcx>
{
    type V = MPlaceTy<'tcx>;

    #[inline(always)]
    fn ecx(&self) -> &CompileTimeEvalContext<'mir, 'tcx> {
        &self.ecx
    }

    fn visit_aggregate(
        &mut self,
        mplace: MPlaceTy<'tcx>,
        fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
    ) -> InterpResult<'tcx> {
        if let Some(def) = mplace.layout.ty.ty_adt_def() {
            if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
                // We are crossing over an `UnsafeCell`, we can mutate again
                let old = std::mem::replace(&mut self.mutability, Mutability::Mutable);
                assert_ne!(
                    self.mode, InternMode::Const,
                    "UnsafeCells are not allowed behind references in constants. This should have \
                    been prevented statically by const qualification. If this were allowed one \
                    would be able to change a constant at one use site and other use sites may \
                    arbitrarily decide to change, too.",
                );
                let walked = self.walk_aggregate(mplace, fields);
                self.mutability = old;
                return walked;
            }
        }
        self.walk_aggregate(mplace, fields)
    }

    fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
        // Handle Reference types, as these are the only relocations supported by const eval.
        // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
        let ty = mplace.layout.ty;
        if let ty::Ref(_, referenced_ty, mutability) = ty.sty {
            let value = self.ecx.read_immediate(mplace.into())?;
            // Handle trait object vtables
            if let Ok(meta) = value.to_meta() {
                if let ty::Dynamic(..) =
                    self.ecx.tcx.struct_tail_erasing_lifetimes(referenced_ty, self.param_env).sty
                {
                    if let Ok(vtable) = meta.unwrap().to_ptr() {
                        // explitly choose `Immutable` here, since vtables are immutable, even
                        // if the reference of the fat pointer is mutable
                        self.intern_shallow(vtable, Mutability::Immutable)?;
                    }
                }
            }
            let mplace = self.ecx.ref_to_mplace(value)?;
            // Check if we have encountered this pointer+layout combination before.
            // Only recurse for allocation-backed pointers.
            if let Scalar::Ptr(ptr) = mplace.ptr {
                // We do not have any `frozen` logic here, because it's essentially equivalent to
                // the mutability except for the outermost item. Only `UnsafeCell` can "unfreeze",
                // and we check that in `visit_aggregate`.
                // This is not an inherent limitation, but one that we know to be true, because
                // const qualification enforces it. We can lift it in the future.
                match (self.mode, mutability) {
                    // immutable references are fine everywhere
                    (_, hir::Mutability::MutImmutable) => {},
                    // all is "good and well" in the unsoundness of `static mut`

                    // mutable references are ok in `static`. Either they are treated as immutable
                    // because they are behind an immutable one, or they are behind an `UnsafeCell`
                    // and thus ok.
                    (InternMode::Static, hir::Mutability::MutMutable) => {},
                    // we statically prevent `&mut T` via `const_qualif` and double check this here
                    (InternMode::ConstBase, hir::Mutability::MutMutable) |
                    (InternMode::Const, hir::Mutability::MutMutable) => {
                        match referenced_ty.sty {
                            ty::Array(_, n) if n.unwrap_usize(self.ecx.tcx.tcx) == 0 => {}
                            ty::Slice(_)
                                if value.to_meta().unwrap().unwrap().to_usize(self.ecx)? == 0 => {}
                            _ => bug!("const qualif failed to prevent mutable references"),
                        }
                    },
                }
                // Compute the mutability with which we'll start visiting the allocation. This is
                // what gets changed when we encounter an `UnsafeCell`
                let mutability = match (self.mutability, mutability) {
                    // The only way a mutable reference actually works as a mutable reference is
                    // by being in a `static mut` directly or behind another mutable reference.
                    // If there's an immutable reference or we are inside a static, then our
                    // mutable reference is equivalent to an immutable one. As an example:
                    // `&&mut Foo` is semantically equivalent to `&&Foo`
                    (Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable,
                    _ => Mutability::Immutable,
                };
                // Compute the mutability of the allocation
                let intern_mutability = intern_mutability(
                    self.ecx.tcx.tcx,
                    self.param_env,
                    mplace.layout.ty,
                    self.ecx.tcx.span,
                    mutability,
                );
                // Recursing behind references changes the intern mode for constants in order to
                // cause assertions to trigger if we encounter any `UnsafeCell`s.
                let mode = match self.mode {
                    InternMode::ConstBase => InternMode::Const,
                    other => other,
                };
                match self.intern_shallow(ptr, intern_mutability)? {
                    // No need to recurse, these are interned already and statics may have
                    // cycles, so we don't want to recurse there
                    Some(IsStaticOrFn) => {},
                    // intern everything referenced by this value. The mutability is taken from the
                    // reference. It is checked above that mutable references only happen in
                    // `static mut`
                    None => self.ref_tracking.track((mplace, mutability, mode), || ()),
                }
            }
        }
        Ok(())
    }
}

/// Figure out the mutability of the allocation.
/// Mutable if it has interior mutability *anywhere* in the type.
fn intern_mutability<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
    ty: Ty<'tcx>,
    span: Span,
    mutability: Mutability,
) -> Mutability {
    let has_interior_mutability = !ty.is_freeze(tcx, param_env, span);
    if has_interior_mutability {
        Mutability::Mutable
    } else {
        mutability
    }
}

pub fn intern_const_alloc_recursive(
    ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
    def_id: DefId,
    ret: MPlaceTy<'tcx>,
    // FIXME(oli-obk): can we scrap the param env? I think we can, the final value of a const eval
    // must always be monomorphic, right?
    param_env: ty::ParamEnv<'tcx>,
) -> InterpResult<'tcx> {
    let tcx = ecx.tcx;
    // this `mutability` is the mutability of the place, ignoring the type
    let (mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
        Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static),
        None => (Mutability::Immutable, InternMode::ConstBase),
        // `static mut` doesn't care about interior mutability, it's mutable anyway
        Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static),
    };

    // type based interning
    let mut ref_tracking = RefTracking::new((ret, mutability, base_intern_mode));
    let leftover_relocations = &mut FxHashSet::default();

    // This mutability is the combination of the place mutability and the type mutability. If either
    // is mutable, `alloc_mutability` is mutable. This exists because the entire allocation needs
    // to be mutable if it contains an `UnsafeCell` anywhere. The other `mutability` exists so that
    // the visitor does not treat everything outside the `UnsafeCell` as mutable.
    let alloc_mutability = intern_mutability(
        tcx.tcx, param_env, ret.layout.ty, tcx.span, mutability,
    );

    // start with the outermost allocation
    InternVisitor {
        ref_tracking: &mut ref_tracking,
        ecx,
        mode: base_intern_mode,
        leftover_relocations,
        param_env,
        mutability,
    }.intern_shallow(ret.ptr.to_ptr()?, alloc_mutability)?;

    while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
        let interned = InternVisitor {
            ref_tracking: &mut ref_tracking,
            ecx,
            mode,
            leftover_relocations,
            param_env,
            mutability,
        }.visit_value(mplace);
        if let Err(error) = interned {
            // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have
            // to read enum discriminants in order to find references in enum variant fields.
            if let InterpError::Unsupported(UnsupportedOpInfo::ValidationFailure(_)) = error.kind {
                let err = crate::const_eval::error_to_const_error(&ecx, error);
                match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
                    Ok(mut diag) => {
                        diag.note("The rules on what exactly is undefined behavior aren't clear, \
                            so this check might be overzealous. Please open an issue on the rust \
                            compiler repository if you believe it should not be considered \
                            undefined behavior",
                        );
                        diag.emit();
                    }
                    Err(ErrorHandled::TooGeneric) |
                    Err(ErrorHandled::Reported) => {},
                }
            }
        }
    }

    // Intern the rest of the allocations as mutable. These might be inside unions, padding, raw
    // pointers, ... So we can't intern them according to their type rules

    let mut todo: Vec<_> = leftover_relocations.iter().cloned().collect();
    while let Some(alloc_id) = todo.pop() {
        if let Some((_, alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) {
            // We can't call the `intern` method here, as its logic is tailored to safe references.
            // So we hand-roll the interning logic here again
            let alloc = tcx.intern_const_alloc(alloc);
            tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
            for &(_, ((), reloc)) in alloc.relocations.iter() {
                if leftover_relocations.insert(reloc) {
                    todo.push(reloc);
                }
            }
        } else if ecx.memory().dead_alloc_map.contains_key(&alloc_id) {
            // dangling pointer
            return throw_err!(ValidationFailure("encountered dangling pointer in final constant".into()))
        }
    }
    Ok(())
}
<commit_msg>tidy fixes<commit_after>//! This module specifies the type based interner for constants.
//!
//! After a const evaluation has computed a value, before we destroy the const evaluator's session
//! memory, we need to extract all memory allocations to the global memory pool so they stay around.

use rustc::ty::{Ty, TyCtxt, ParamEnv, self};
use rustc::mir::interpret::{
    InterpResult, ErrorHandled, UnsupportedOpInfo,
};
use rustc::hir;
use rustc::hir::def_id::DefId;
use super::validity::RefTracking;
use rustc_data_structures::fx::FxHashSet;

use syntax::ast::Mutability;
use syntax_pos::Span;

use super::{
    ValueVisitor, MemoryKind, Pointer, AllocId, MPlaceTy, InterpError, Scalar,
};
use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};

struct InternVisitor<'rt, 'mir, 'tcx> {
    /// previously encountered safe references
    ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
    ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
    param_env: ParamEnv<'tcx>,
    /// The root node of the value that we're looking at. This field is never mutated and only used
    /// for sanity assertions that will ICE when `const_qualif` screws up.
    mode: InternMode,
    /// This field stores the mutability of the value *currently* being checked.
    /// It is set to mutable when an `UnsafeCell` is encountered
    /// When recursing across a reference, we don't recurse but store the
    /// value to be checked in `ref_tracking` together with the mutability at which we are checking
    /// the value.
    /// When encountering an immutable reference, we treat everything as immutable that is behind
    /// it.
    mutability: Mutability,
    /// A list of all encountered relocations. After type-based interning, we traverse this list to
    /// also intern allocations that are only referenced by a raw pointer or inside a union.
    leftover_relocations: &'rt mut FxHashSet<AllocId>,
}

#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
enum InternMode {
    /// Mutable references must in fact be immutable due to their surrounding immutability in a
    /// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
    /// that will actually be treated as mutable.
    Static,
    /// UnsafeCell is OK in the value of a constant, but not behind references in a constant
    ConstBase,
    /// `UnsafeCell` ICEs
    Const,
}

/// Signalling data structure to ensure we don't recurse
/// into the memory of other constants or statics
struct IsStaticOrFn;

impl<'rt, 'mir, 'tcx> InternVisitor<'rt, 'mir, 'tcx> {
    /// Intern an allocation without looking at its children
    fn intern_shallow(
        &mut self,
        ptr: Pointer,
        mutability: Mutability,
    ) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
        trace!(
            "InternVisitor::intern {:?} with {:?}",
            ptr, mutability,
        );
        // remove allocation
        let tcx = self.ecx.tcx;
        let memory = self.ecx.memory_mut();
        let (kind, mut alloc) = match memory.alloc_map.remove(&ptr.alloc_id) {
            Some(entry) => entry,
            None => {
                // if the pointer is dangling (neither in local nor global memory), we leave it
                // to validation to error. The `delay_span_bug` ensures that we don't forget such
                // a check in validation.
                if tcx.alloc_map.lock().get(ptr.alloc_id).is_none() {
                    tcx.sess.delay_span_bug(self.ecx.tcx.span, "tried to intern dangling pointer");
                }
                // treat dangling pointers like other statics
                // just to stop trying to recurse into them
                return Ok(Some(IsStaticOrFn));
            },
        };
        // This match is just a canary for future changes to `MemoryKind`, which most likely need
        // changes in this function.
        match kind {
            MemoryKind::Stack | MemoryKind::Vtable => {},
        }
        // Ensure llvm knows to only put this into immutable memory if the value is immutable either
        // by being behind a reference or by being part of a static or const without interior
        // mutability
        alloc.mutability = mutability;
        // link the alloc id to the actual allocation
        let alloc = tcx.intern_const_alloc(alloc);
        self.leftover_relocations.extend(alloc.relocations.iter().map(|&(_, ((), reloc))| reloc));
        tcx.alloc_map.lock().set_alloc_id_memory(ptr.alloc_id, alloc);
        Ok(None)
    }
}

impl<'rt, 'mir, 'tcx>
    ValueVisitor<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>
for
    InternVisitor<'rt, 'mir, 'tcx>
{
    type V = MPlaceTy<'tcx>;

    #[inline(always)]
    fn ecx(&self) -> &CompileTimeEvalContext<'mir, 'tcx> {
        &self.ecx
    }

    fn visit_aggregate(
        &mut self,
        mplace: MPlaceTy<'tcx>,
        fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
    ) -> InterpResult<'tcx> {
        if let Some(def) = mplace.layout.ty.ty_adt_def() {
            if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
                // We are crossing over an `UnsafeCell`, we can mutate again
                let old = std::mem::replace(&mut self.mutability, Mutability::Mutable);
                assert_ne!(
                    self.mode, InternMode::Const,
                    "UnsafeCells are not allowed behind references in constants. This should have \
                    been prevented statically by const qualification. If this were allowed one \
                    would be able to change a constant at one use site and other use sites may \
                    arbitrarily decide to change, too.",
                );
                let walked = self.walk_aggregate(mplace, fields);
                self.mutability = old;
                return walked;
            }
        }
        self.walk_aggregate(mplace, fields)
    }

    fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
        // Handle Reference types, as these are the only relocations supported by const eval.
        // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
        let ty = mplace.layout.ty;
        if let ty::Ref(_, referenced_ty, mutability) = ty.sty {
            let value = self.ecx.read_immediate(mplace.into())?;
            // Handle trait object vtables
            if let Ok(meta) = value.to_meta() {
                if let ty::Dynamic(..) =
                    self.ecx.tcx.struct_tail_erasing_lifetimes(referenced_ty, self.param_env).sty
                {
                    if let Ok(vtable) = meta.unwrap().to_ptr() {
                        // explitly choose `Immutable` here, since vtables are immutable, even
                        // if the reference of the fat pointer is mutable
                        self.intern_shallow(vtable, Mutability::Immutable)?;
                    }
                }
            }
            let mplace = self.ecx.ref_to_mplace(value)?;
            // Check if we have encountered this pointer+layout combination before.
            // Only recurse for allocation-backed pointers.
            if let Scalar::Ptr(ptr) = mplace.ptr {
                // We do not have any `frozen` logic here, because it's essentially equivalent to
                // the mutability except for the outermost item. Only `UnsafeCell` can "unfreeze",
                // and we check that in `visit_aggregate`.
                // This is not an inherent limitation, but one that we know to be true, because
                // const qualification enforces it. We can lift it in the future.
                match (self.mode, mutability) {
                    // immutable references are fine everywhere
                    (_, hir::Mutability::MutImmutable) => {},
                    // all is "good and well" in the unsoundness of `static mut`

                    // mutable references are ok in `static`. Either they are treated as immutable
                    // because they are behind an immutable one, or they are behind an `UnsafeCell`
                    // and thus ok.
                    (InternMode::Static, hir::Mutability::MutMutable) => {},
                    // we statically prevent `&mut T` via `const_qualif` and double check this here
                    (InternMode::ConstBase, hir::Mutability::MutMutable) |
                    (InternMode::Const, hir::Mutability::MutMutable) => {
                        match referenced_ty.sty {
                            ty::Array(_, n) if n.unwrap_usize(self.ecx.tcx.tcx) == 0 => {}
                            ty::Slice(_)
                                if value.to_meta().unwrap().unwrap().to_usize(self.ecx)? == 0 => {}
                            _ => bug!("const qualif failed to prevent mutable references"),
                        }
                    },
                }
                // Compute the mutability with which we'll start visiting the allocation. This is
                // what gets changed when we encounter an `UnsafeCell`
                let mutability = match (self.mutability, mutability) {
                    // The only way a mutable reference actually works as a mutable reference is
                    // by being in a `static mut` directly or behind another mutable reference.
                    // If there's an immutable reference or we are inside a static, then our
                    // mutable reference is equivalent to an immutable one. As an example:
                    // `&&mut Foo` is semantically equivalent to `&&Foo`
                    (Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable,
                    _ => Mutability::Immutable,
                };
                // Compute the mutability of the allocation
                let intern_mutability = intern_mutability(
                    self.ecx.tcx.tcx,
                    self.param_env,
                    mplace.layout.ty,
                    self.ecx.tcx.span,
                    mutability,
                );
                // Recursing behind references changes the intern mode for constants in order to
                // cause assertions to trigger if we encounter any `UnsafeCell`s.
                let mode = match self.mode {
                    InternMode::ConstBase => InternMode::Const,
                    other => other,
                };
                match self.intern_shallow(ptr, intern_mutability)? {
                    // No need to recurse, these are interned already and statics may have
                    // cycles, so we don't want to recurse there
                    Some(IsStaticOrFn) => {},
                    // intern everything referenced by this value. The mutability is taken from the
                    // reference. It is checked above that mutable references only happen in
                    // `static mut`
                    None => self.ref_tracking.track((mplace, mutability, mode), || ()),
                }
            }
        }
        Ok(())
    }
}

/// Figure out the mutability of the allocation.
/// Mutable if it has interior mutability *anywhere* in the type.
fn intern_mutability<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
    ty: Ty<'tcx>,
    span: Span,
    mutability: Mutability,
) -> Mutability {
    let has_interior_mutability = !ty.is_freeze(tcx, param_env, span);
    if has_interior_mutability {
        Mutability::Mutable
    } else {
        mutability
    }
}

pub fn intern_const_alloc_recursive(
    ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
    def_id: DefId,
    ret: MPlaceTy<'tcx>,
    // FIXME(oli-obk): can we scrap the param env? I think we can, the final value of a const eval
    // must always be monomorphic, right?
    param_env: ty::ParamEnv<'tcx>,
) -> InterpResult<'tcx> {
    let tcx = ecx.tcx;
    // this `mutability` is the mutability of the place, ignoring the type
    let (mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
        Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static),
        None => (Mutability::Immutable, InternMode::ConstBase),
        // `static mut` doesn't care about interior mutability, it's mutable anyway
        Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static),
    };

    // type based interning
    let mut ref_tracking = RefTracking::new((ret, mutability, base_intern_mode));
    let leftover_relocations = &mut FxHashSet::default();

    // This mutability is the combination of the place mutability and the type mutability. If either
    // is mutable, `alloc_mutability` is mutable. This exists because the entire allocation needs
    // to be mutable if it contains an `UnsafeCell` anywhere. The other `mutability` exists so that
    // the visitor does not treat everything outside the `UnsafeCell` as mutable.
    let alloc_mutability = intern_mutability(
        tcx.tcx, param_env, ret.layout.ty, tcx.span, mutability,
    );

    // start with the outermost allocation
    InternVisitor {
        ref_tracking: &mut ref_tracking,
        ecx,
        mode: base_intern_mode,
        leftover_relocations,
        param_env,
        mutability,
    }.intern_shallow(ret.ptr.to_ptr()?, alloc_mutability)?;

    while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
        let interned = InternVisitor {
            ref_tracking: &mut ref_tracking,
            ecx,
            mode,
            leftover_relocations,
            param_env,
            mutability,
        }.visit_value(mplace);
        if let Err(error) = interned {
            // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have
            // to read enum discriminants in order to find references in enum variant fields.
            if let InterpError::Unsupported(UnsupportedOpInfo::ValidationFailure(_)) = error.kind {
                let err = crate::const_eval::error_to_const_error(&ecx, error);
                match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
                    Ok(mut diag) => {
                        diag.note("The rules on what exactly is undefined behavior aren't clear, \
                            so this check might be overzealous. Please open an issue on the rust \
                            compiler repository if you believe it should not be considered \
                            undefined behavior",
                        );
                        diag.emit();
                    }
                    Err(ErrorHandled::TooGeneric) |
                    Err(ErrorHandled::Reported) => {},
                }
            }
        }
    }

    // Intern the rest of the allocations as mutable. These might be inside unions, padding, raw
    // pointers, ... So we can't intern them according to their type rules

    let mut todo: Vec<_> = leftover_relocations.iter().cloned().collect();
    while let Some(alloc_id) = todo.pop() {
        if let Some((_, alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) {
            // We can't call the `intern` method here, as its logic is tailored to safe references.
            // So we hand-roll the interning logic here again
            let alloc = tcx.intern_const_alloc(alloc);
            tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
            for &(_, ((), reloc)) in alloc.relocations.iter() {
                if leftover_relocations.insert(reloc) {
                    todo.push(reloc);
                }
            }
        } else if ecx.memory().dead_alloc_map.contains_key(&alloc_id) {
            // dangling pointer
            return throw_err!(
                ValidationFailure("encountered dangling pointer in final constant".into())
            )
        }
    }
    Ok(())
}
<|endoftext|>