<commit_before>//! Simple benchmarks, from https://github.com/aepsil0n/carboxyl

use bencher::{Bencher, benchmark_group, benchmark_main};
use frappe::Sink;

fn bench_chain(b: &mut Bencher) {
    let sink: Sink<i32> = Sink::new();
    let sig = sink.stream()
        .map(|x| *x + 4)
        .filter(|&x| x < 4)
        .merge(&sink.stream().map(|x| *x * 5))
        .hold(15);

    b.iter(|| {
        sink.send(-5);
        sig.sample()
    });
}

benchmark_group!(simple, bench_chain);
benchmark_main!(simple);
<commit_msg>updated simple bench<commit_after>//! Simple benchmarks, from https://github.com/aepsil0n/carboxyl

use bencher::{Bencher, benchmark_group, benchmark_main, black_box};
use frappe::{Sink, Signal};

fn make_chain() -> (Sink<i32>, Signal<i32>) {
    let sink = Sink::new();
    let sig = sink.stream()
        .map(|x| *x + 4)
        .filter(|&x| x < 4)
        .merge(&sink.stream().map(|x| *x * 5))
        .hold(15)
        .map(|x| *x + 1);
    (sink, sig)
}

fn send(b: &mut Bencher) {
    let (sink, sig) = make_chain();
    b.iter(|| sink.send(-5));
    black_box(sig.sample());
}

fn sample(b: &mut Bencher) {
    let (sink, sig) = make_chain();
    sink.send(-5);
    b.iter(|| sig.sample());
}

fn send_and_sample(b: &mut Bencher) {
    let (sink, sig) = make_chain();
    b.iter(|| {
        sink.send(-5);
        sig.sample()
    });
}

benchmark_group!(simple, send, sample, send_and_sample);
benchmark_main!(simple);
<|endoftext|>


<commit_before>use rustc_serialize::Decoder;
use rustc_serialize::Decodable;

use Request;
use RouteError;

use std::mem;
use std::num;
use std::str::ParseBoolError;
use url::form_urlencoded;

#[derive(Clone, Debug)]
pub enum PostError {
    WrongContentType,

    MissingField(String),

    WrongDataTypeBool(ParseBoolError),

    WrongDataTypeInt(num::ParseIntError),

    WrongDataTypeFloat(num::ParseFloatError),

    NotUtf8(String),
}

impl From<PostError> for RouteError {
    #[inline]
    fn from(err: PostError) -> RouteError {
        RouteError::WrongInput
    }
}

impl From<ParseBoolError> for PostError {
    #[inline]
    fn from(err: ParseBoolError) -> PostError {
        PostError::WrongDataTypeBool(err)
    }
}

impl From<num::ParseIntError> for PostError {
    #[inline]
    fn from(err: num::ParseIntError) -> PostError {
        PostError::WrongDataTypeInt(err)
    }
}

impl From<num::ParseFloatError> for PostError {
    #[inline]
    fn from(err: num::ParseFloatError) -> PostError {
        PostError::WrongDataTypeFloat(err)
    }
}

pub fn get_post_input<T>(request: &Request) -> Result<T, PostError> where T: Decodable {
    // TODO: slow
    if request.header("Content-Type") != Some("application/x-www-form-urlencoded".to_owned()) {
        return Err(PostError::WrongContentType);
    }

    let data = form_urlencoded::parse(&request.data());
    let mut decoder = PostDecoder::Start(data);
    T::decode(&mut decoder)
}

enum PostDecoder {
    Empty,

    Start(Vec<(String, String)>),

    ExpectsStructMember(Vec<(String, String)>),

    ExpectsData(Vec<(String, String)>, String),
}

impl Decoder for PostDecoder {
    type Error = PostError;

    fn read_usize(&mut self) -> Result<usize, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_u64(&mut self) -> Result<u64, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_u32(&mut self) -> Result<u32, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_u16(&mut self) -> Result<u16, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_u8(&mut self) -> Result<u8, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_isize(&mut self) -> Result<isize, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_i64(&mut self) -> Result<i64, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_i32(&mut self) -> Result<i32, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_i16(&mut self) -> Result<i16, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_i8(&mut self) -> Result<i8, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_bool(&mut self) -> Result<bool, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_f64(&mut self) -> Result<f64, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_f32(&mut self) -> Result<f32, PostError> { Ok(try!(try!(self.read_str()).parse())) }

    fn read_char(&mut self) -> Result<char, PostError> {
        unimplemented!();
    }

    fn read_str(&mut self) -> Result<String, PostError> {
        match self {
            &mut PostDecoder::ExpectsData(ref data, ref field_name) => {
                let val = data.iter().find(|&&(ref key, _)| key == field_name)
                              .map(|&(_, ref value)| value);

                if let Some(val) = val {
                    Ok(val.clone())
                } else {
                    Err(PostError::MissingField(field_name.clone()))
                }
            },

            _ => panic!()
        }
    }

    fn read_nil(&mut self) -> Result<(), PostError> {
        unimplemented!();
    }

    fn read_enum<T, F>(&mut self, name: &str, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_enum_variant<T, F>(&mut self, names: &[&str], f: F) -> Result<T, PostError> where F: FnMut(&mut Self, usize) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_enum_variant_arg<T, F>(&mut self, a_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_enum_struct_variant<T, F>(&mut self, names: &[&str], f: F) -> Result<T, PostError> where F: FnMut(&mut Self, usize) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_enum_struct_variant_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_struct<T, F>(&mut self, s_name: &str, len: usize, mut f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        let mut tmp = match mem::replace(self, PostDecoder::Empty) {
            PostDecoder::Start(data) => PostDecoder::ExpectsStructMember(data),
            _ => panic!()
        };

        f(&mut tmp)
    }

    fn read_struct_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        let mut tmp = match mem::replace(self, PostDecoder::Empty) {
            PostDecoder::ExpectsStructMember(data) => PostDecoder::ExpectsData(data, f_name.to_owned()),
            _ => panic!()
        };

        let result = f(&mut tmp);

        match tmp {
            PostDecoder::ExpectsData(data, _) => mem::replace(self, PostDecoder::ExpectsStructMember(data)),
            _ => panic!()
        };

        result
    }

    fn read_tuple<T, F>(&mut self, len: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_tuple_arg<T, F>(&mut self, a_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_tuple_struct<T, F>(&mut self, s_name: &str, len: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_tuple_struct_arg<T, F>(&mut self, a_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_option<T, F>(&mut self, f: F) -> Result<T, PostError> where F: FnMut(&mut Self, bool) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_seq<T, F>(&mut self, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self, usize) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_seq_elt<T, F>(&mut self, idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_map<T, F>(&mut self, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self, usize) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn error(&mut self, err: &str) -> PostError {
        unimplemented!();
    }
}
<commit_msg>Add docs to input/post.rs<commit_after>use rustc_serialize::Decoder;
use rustc_serialize::Decodable;

use Request;
use RouteError;

use std::mem;
use std::num;
use std::str::ParseBoolError;
use url::form_urlencoded;

/// Error that can happen when decoding POST data.
#[derive(Clone, Debug)]
pub enum PostError {
    /// The `Content-Type` header of the request indicates that it doesn't contain POST data.
    WrongContentType,

    /// A field is missing from the received data.
    MissingField(String),

    /// Failed to parse a `bool` field.
    WrongDataTypeBool(ParseBoolError),

    /// Failed to parse an integer field.
    WrongDataTypeInt(num::ParseIntError),

    /// Failed to parse a floating-point field.
    WrongDataTypeFloat(num::ParseFloatError),

    /// Failed to parse a string field.
    NotUtf8(String),
}

impl From<PostError> for RouteError {
    #[inline]
    fn from(err: PostError) -> RouteError {
        RouteError::WrongInput
    }
}

impl From<ParseBoolError> for PostError {
    #[inline]
    fn from(err: ParseBoolError) -> PostError {
        PostError::WrongDataTypeBool(err)
    }
}

impl From<num::ParseIntError> for PostError {
    #[inline]
    fn from(err: num::ParseIntError) -> PostError {
        PostError::WrongDataTypeInt(err)
    }
}

impl From<num::ParseFloatError> for PostError {
    #[inline]
    fn from(err: num::ParseFloatError) -> PostError {
        PostError::WrongDataTypeFloat(err)
    }
}

/// Attempts to decode the `POST` data received by the request into a struct.
///
/// The struct must implement the `Decodable` trait from `rustc_serialize`.
///
/// An error is returned if a field is missing, if the content type is not POST data, or if a field
/// cannot be parsed.
///
/// # Example
///
/// ```no_run
/// # extern crate rustc_serialize;
/// # extern crate rouille;
/// # fn main() {
/// # let request: rouille::Request = unsafe { std::mem::uninitialized() };
/// #[derive(RustcDecodable)]
/// struct FormData {
///     field1: u32,
///     field2: String,
/// }
///
/// let data: FormData = rouille::input::get_post_input(&request).unwrap();
/// # }
/// ```
///
pub fn get_post_input<T>(request: &Request) -> Result<T, PostError> where T: Decodable {
    // TODO: slow
    if request.header("Content-Type") != Some("application/x-www-form-urlencoded".to_owned()) {
        return Err(PostError::WrongContentType);
    }

    let data = form_urlencoded::parse(&request.data());
    let mut decoder = PostDecoder::Start(data);
    T::decode(&mut decoder)
}

enum PostDecoder {
    Empty,

    Start(Vec<(String, String)>),

    ExpectsStructMember(Vec<(String, String)>),

    ExpectsData(Vec<(String, String)>, String),
}

impl Decoder for PostDecoder {
    type Error = PostError;

    fn read_usize(&mut self) -> Result<usize, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_u64(&mut self) -> Result<u64, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_u32(&mut self) -> Result<u32, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_u16(&mut self) -> Result<u16, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_u8(&mut self) -> Result<u8, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_isize(&mut self) -> Result<isize, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_i64(&mut self) -> Result<i64, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_i32(&mut self) -> Result<i32, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_i16(&mut self) -> Result<i16, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_i8(&mut self) -> Result<i8, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_bool(&mut self) -> Result<bool, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_f64(&mut self) -> Result<f64, PostError> { Ok(try!(try!(self.read_str()).parse())) }
    fn read_f32(&mut self) -> Result<f32, PostError> { Ok(try!(try!(self.read_str()).parse())) }

    fn read_char(&mut self) -> Result<char, PostError> {
        unimplemented!();
    }

    fn read_str(&mut self) -> Result<String, PostError> {
        match self {
            &mut PostDecoder::ExpectsData(ref data, ref field_name) => {
                let val = data.iter().find(|&&(ref key, _)| key == field_name)
                              .map(|&(_, ref value)| value);

                if let Some(val) = val {
                    Ok(val.clone())
                } else {
                    Err(PostError::MissingField(field_name.clone()))
                }
            },

            _ => panic!()
        }
    }

    fn read_nil(&mut self) -> Result<(), PostError> {
        unimplemented!();
    }

    fn read_enum<T, F>(&mut self, name: &str, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_enum_variant<T, F>(&mut self, names: &[&str], f: F) -> Result<T, PostError> where F: FnMut(&mut Self, usize) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_enum_variant_arg<T, F>(&mut self, a_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_enum_struct_variant<T, F>(&mut self, names: &[&str], f: F) -> Result<T, PostError> where F: FnMut(&mut Self, usize) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_enum_struct_variant_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_struct<T, F>(&mut self, s_name: &str, len: usize, mut f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        let mut tmp = match mem::replace(self, PostDecoder::Empty) {
            PostDecoder::Start(data) => PostDecoder::ExpectsStructMember(data),
            _ => panic!()
        };

        f(&mut tmp)
    }

    fn read_struct_field<T, F>(&mut self, f_name: &str, f_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        let mut tmp = match mem::replace(self, PostDecoder::Empty) {
            PostDecoder::ExpectsStructMember(data) => PostDecoder::ExpectsData(data, f_name.to_owned()),
            _ => panic!()
        };

        let result = f(&mut tmp);

        match tmp {
            PostDecoder::ExpectsData(data, _) => mem::replace(self, PostDecoder::ExpectsStructMember(data)),
            _ => panic!()
        };

        result
    }

    fn read_tuple<T, F>(&mut self, len: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_tuple_arg<T, F>(&mut self, a_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_tuple_struct<T, F>(&mut self, s_name: &str, len: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_tuple_struct_arg<T, F>(&mut self, a_idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_option<T, F>(&mut self, f: F) -> Result<T, PostError> where F: FnMut(&mut Self, bool) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_seq<T, F>(&mut self, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self, usize) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_seq_elt<T, F>(&mut self, idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_map<T, F>(&mut self, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self, usize) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_map_elt_key<T, F>(&mut self, idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn read_map_elt_val<T, F>(&mut self, idx: usize, f: F) -> Result<T, PostError> where F: FnOnce(&mut Self) -> Result<T, PostError> {
        unimplemented!();
    }

    fn error(&mut self, err: &str) -> PostError {
        unimplemented!();
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::hash_map::Entry;

use log::trace;
use rand::Rng;

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_target::abi::{HasDataLayout, Size};

use crate::*;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ProvenanceMode {
    /// We support `expose_addr`/`from_exposed_addr` via "wildcard" provenance.
    /// However, we want on `from_exposed_addr` to alert the user of the precision loss.
    Default,
    /// Like `Default`, but without the warning.
    Permissive,
    /// We error on `from_exposed_addr`, ensuring no precision loss.
    Strict,
}

pub type GlobalState = RefCell<GlobalStateInner>;

#[derive(Clone, Debug)]
pub struct GlobalStateInner {
    /// This is used as a map between the address of each allocation and its `AllocId`.
    /// It is always sorted
    int_to_ptr_map: Vec<(u64, AllocId)>,
    /// The base address for each allocation.  We cannot put that into
    /// `AllocExtra` because function pointers also have a base address, and
    /// they do not have an `AllocExtra`.
    /// This is the inverse of `int_to_ptr_map`.
    base_addr: FxHashMap<AllocId, u64>,
    /// Whether an allocation has been exposed or not. This cannot be put
    /// into `AllocExtra` for the same reason as `base_addr`.
    exposed: FxHashSet<AllocId>,
    /// This is used as a memory address when a new pointer is casted to an integer. It
    /// is always larger than any address that was previously made part of a block.
    next_base_addr: u64,
    /// The provenance to use for int2ptr casts
    provenance_mode: ProvenanceMode,
}

impl GlobalStateInner {
    pub fn new(config: &MiriConfig) -> Self {
        GlobalStateInner {
            int_to_ptr_map: Vec::default(),
            base_addr: FxHashMap::default(),
            exposed: FxHashSet::default(),
            next_base_addr: STACK_ADDR,
            provenance_mode: config.provenance_mode,
        }
    }
}

impl<'mir, 'tcx> GlobalStateInner {
    // Returns the exposed `AllocId` that corresponds to the specified addr,
    // or `None` if the addr is out of bounds
    fn alloc_id_from_addr(ecx: &MiriEvalContext<'mir, 'tcx>, addr: u64) -> Option<AllocId> {
        let global_state = ecx.machine.intptrcast.borrow();
        assert!(global_state.provenance_mode != ProvenanceMode::Strict);

        let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr);

        // Determine the in-bounds provenance for this pointer.
        // (This is only called on an actual access, so in-bounds is the only possible kind of provenance.)
        let alloc_id = match pos {
            Ok(pos) => Some(global_state.int_to_ptr_map[pos].1),
            Err(0) => None,
            Err(pos) => {
                // This is the largest of the adresses smaller than `int`,
                // i.e. the greatest lower bound (glb)
                let (glb, alloc_id) = global_state.int_to_ptr_map[pos - 1];
                // This never overflows because `addr >= glb`
                let offset = addr - glb;
                // If the offset exceeds the size of the allocation, don't use this `alloc_id`.

                if offset
                    <= ecx
                        .get_alloc_size_and_align(alloc_id, AllocCheck::MaybeDead)
                        .unwrap()
                        .0
                        .bytes()
                {
                    Some(alloc_id)
                } else {
                    None
                }
            }
        }?;

        // We only use this provenance if it has been exposed, *and* is still live.
        if global_state.exposed.contains(&alloc_id) {
            // FIXME: this catches `InterpError`, which we should not usually do.
            // We might need a proper fallible API from `memory.rs` to avoid this though.
            if ecx.get_alloc_size_and_align(alloc_id, AllocCheck::Live).is_ok() {
                return Some(alloc_id);
            }
        }

        None
    }

    pub fn expose_ptr(ecx: &mut MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId, sb: SbTag) {
        let global_state = ecx.machine.intptrcast.get_mut();
        // In strict mode, we don't need this, so we can save some cycles by not tracking it.
        if global_state.provenance_mode != ProvenanceMode::Strict {
            trace!("Exposing allocation id {alloc_id:?}");
            global_state.exposed.insert(alloc_id);
            if ecx.machine.stacked_borrows.is_some() {
                ecx.expose_tag(alloc_id, sb);
            }
        }
    }

    pub fn ptr_from_addr_transmute(
        ecx: &MiriEvalContext<'mir, 'tcx>,
        addr: u64,
    ) -> Pointer<Option<Tag>> {
        trace!("Transmuting 0x{:x} to a pointer", addr);

        let provenance = if ecx.machine.allow_ptr_int_transmute {
            // When we allow transmutes, treat them like casts: generating a wildcard pointer.
            Some(Tag::Wildcard)
        } else {
            // Usually, we consider transmuted pointers to be "invalid" (`None` provenance).
            None
        };
        Pointer::new(provenance, Size::from_bytes(addr))
    }

    pub fn ptr_from_addr_cast(
        ecx: &MiriEvalContext<'mir, 'tcx>,
        addr: u64,
    ) -> InterpResult<'tcx, Pointer<Option<Tag>>> {
        trace!("Casting 0x{:x} to a pointer", addr);

        let global_state = ecx.machine.intptrcast.borrow();

        match global_state.provenance_mode {
            ProvenanceMode::Default => {
                // The first time this happens, print a warning.
                use std::sync::atomic::{AtomicBool, Ordering};
                static FIRST_WARNING: AtomicBool = AtomicBool::new(true);
                if FIRST_WARNING.swap(false, Ordering::Relaxed) {
                    register_diagnostic(NonHaltingDiagnostic::Int2Ptr);
                }
            }
            ProvenanceMode::Strict => {
                throw_unsup_format!(
                    "integer-to-pointer casts and `from_exposed_addr` are not supported with `-Zmiri-strict-provenance`; use `with_addr` instead"
                )
            }
            ProvenanceMode::Permissive => {}
        }

        // This is how wildcard pointers are born.
        Ok(Pointer::new(Some(Tag::Wildcard), Size::from_bytes(addr)))
    }

    fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 {
        let mut global_state = ecx.machine.intptrcast.borrow_mut();
        let global_state = &mut *global_state;

        match global_state.base_addr.entry(alloc_id) {
            Entry::Occupied(entry) => *entry.get(),
            Entry::Vacant(entry) => {
                // There is nothing wrong with a raw pointer being cast to an integer only after
                // it became dangling.  Hence `MaybeDead`.
                let (size, align) =
                    ecx.get_alloc_size_and_align(alloc_id, AllocCheck::MaybeDead).unwrap();

                // This allocation does not have a base address yet, pick one.
                // Leave some space to the previous allocation, to give it some chance to be less aligned.
                let slack = {
                    let mut rng = ecx.machine.rng.borrow_mut();
                    // This means that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
                    rng.gen_range(0..16)
                };
                // From next_base_addr + slack, round up to adjust for alignment.
                let base_addr = global_state.next_base_addr.checked_add(slack).unwrap();
                let base_addr = Self::align_addr(base_addr, align.bytes());
                entry.insert(base_addr);
                trace!(
                    "Assigning base address {:#x} to allocation {:?} (size: {}, align: {}, slack: {})",
                    base_addr,
                    alloc_id,
                    size.bytes(),
                    align.bytes(),
                    slack,
                );

                // Remember next base address.  Leave a gap of at least 1 to avoid two zero-sized allocations
                // having the same base address, and to avoid ambiguous provenance for the address between two
                // allocations (also see https://github.com/rust-lang/unsafe-code-guidelines/issues/313).
                let size_plus_1 = size.bytes().checked_add(1).unwrap();
                global_state.next_base_addr = base_addr.checked_add(size_plus_1).unwrap();
                // Given that `next_base_addr` increases in each allocation, pushing the
                // corresponding tuple keeps `int_to_ptr_map` sorted
                global_state.int_to_ptr_map.push((base_addr, alloc_id));

                base_addr
            }
        }
    }

    /// Convert a relative (tcx) pointer to an absolute address.
    pub fn rel_ptr_to_addr(ecx: &MiriEvalContext<'mir, 'tcx>, ptr: Pointer<AllocId>) -> u64 {
        let (alloc_id, offset) = ptr.into_parts(); // offset is relative (AllocId provenance)
        let base_addr = GlobalStateInner::alloc_base_addr(ecx, alloc_id);

        // Add offset with the right kind of pointer-overflowing arithmetic.
        let dl = ecx.data_layout();
        dl.overflowing_offset(base_addr, offset.bytes()).0
    }

    /// Whena  pointer is used for a memory access, this computes where in which allocation the
    /// access is going.
    pub fn abs_ptr_to_rel(
        ecx: &MiriEvalContext<'mir, 'tcx>,
        ptr: Pointer<Tag>,
    ) -> Option<(AllocId, Size)> {
        let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance)

        let alloc_id = if let Tag::Concrete { alloc_id, .. } = tag {
            alloc_id
        } else {
            // A wildcard pointer.
            GlobalStateInner::alloc_id_from_addr(ecx, addr.bytes())?
        };

        let base_addr = GlobalStateInner::alloc_base_addr(ecx, alloc_id);

        // Wrapping "addr - base_addr"
        let dl = ecx.data_layout();
        let neg_base_addr = (base_addr as i64).wrapping_neg();
        Some((
            alloc_id,
            Size::from_bytes(dl.overflowing_signed_offset(addr.bytes(), neg_base_addr).0),
        ))
    }

    /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
    /// of `align` that is larger or equal to `addr`
    fn align_addr(addr: u64, align: u64) -> u64 {
        match addr % align {
            0 => addr,
            rem => addr.checked_add(align).unwrap() - rem,
        }
    }
}

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

    #[test]
    fn test_align_addr() {
        assert_eq!(GlobalStateInner::align_addr(37, 4), 40);
        assert_eq!(GlobalStateInner::align_addr(44, 4), 44);
    }
}
<commit_msg>typo<commit_after>use std::cell::RefCell;
use std::collections::hash_map::Entry;

use log::trace;
use rand::Rng;

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_target::abi::{HasDataLayout, Size};

use crate::*;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ProvenanceMode {
    /// We support `expose_addr`/`from_exposed_addr` via "wildcard" provenance.
    /// However, we want on `from_exposed_addr` to alert the user of the precision loss.
    Default,
    /// Like `Default`, but without the warning.
    Permissive,
    /// We error on `from_exposed_addr`, ensuring no precision loss.
    Strict,
}

pub type GlobalState = RefCell<GlobalStateInner>;

#[derive(Clone, Debug)]
pub struct GlobalStateInner {
    /// This is used as a map between the address of each allocation and its `AllocId`.
    /// It is always sorted
    int_to_ptr_map: Vec<(u64, AllocId)>,
    /// The base address for each allocation.  We cannot put that into
    /// `AllocExtra` because function pointers also have a base address, and
    /// they do not have an `AllocExtra`.
    /// This is the inverse of `int_to_ptr_map`.
    base_addr: FxHashMap<AllocId, u64>,
    /// Whether an allocation has been exposed or not. This cannot be put
    /// into `AllocExtra` for the same reason as `base_addr`.
    exposed: FxHashSet<AllocId>,
    /// This is used as a memory address when a new pointer is casted to an integer. It
    /// is always larger than any address that was previously made part of a block.
    next_base_addr: u64,
    /// The provenance to use for int2ptr casts
    provenance_mode: ProvenanceMode,
}

impl GlobalStateInner {
    pub fn new(config: &MiriConfig) -> Self {
        GlobalStateInner {
            int_to_ptr_map: Vec::default(),
            base_addr: FxHashMap::default(),
            exposed: FxHashSet::default(),
            next_base_addr: STACK_ADDR,
            provenance_mode: config.provenance_mode,
        }
    }
}

impl<'mir, 'tcx> GlobalStateInner {
    // Returns the exposed `AllocId` that corresponds to the specified addr,
    // or `None` if the addr is out of bounds
    fn alloc_id_from_addr(ecx: &MiriEvalContext<'mir, 'tcx>, addr: u64) -> Option<AllocId> {
        let global_state = ecx.machine.intptrcast.borrow();
        assert!(global_state.provenance_mode != ProvenanceMode::Strict);

        let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr);

        // Determine the in-bounds provenance for this pointer.
        // (This is only called on an actual access, so in-bounds is the only possible kind of provenance.)
        let alloc_id = match pos {
            Ok(pos) => Some(global_state.int_to_ptr_map[pos].1),
            Err(0) => None,
            Err(pos) => {
                // This is the largest of the adresses smaller than `int`,
                // i.e. the greatest lower bound (glb)
                let (glb, alloc_id) = global_state.int_to_ptr_map[pos - 1];
                // This never overflows because `addr >= glb`
                let offset = addr - glb;
                // If the offset exceeds the size of the allocation, don't use this `alloc_id`.

                if offset
                    <= ecx
                        .get_alloc_size_and_align(alloc_id, AllocCheck::MaybeDead)
                        .unwrap()
                        .0
                        .bytes()
                {
                    Some(alloc_id)
                } else {
                    None
                }
            }
        }?;

        // We only use this provenance if it has been exposed, *and* is still live.
        if global_state.exposed.contains(&alloc_id) {
            // FIXME: this catches `InterpError`, which we should not usually do.
            // We might need a proper fallible API from `memory.rs` to avoid this though.
            if ecx.get_alloc_size_and_align(alloc_id, AllocCheck::Live).is_ok() {
                return Some(alloc_id);
            }
        }

        None
    }

    pub fn expose_ptr(ecx: &mut MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId, sb: SbTag) {
        let global_state = ecx.machine.intptrcast.get_mut();
        // In strict mode, we don't need this, so we can save some cycles by not tracking it.
        if global_state.provenance_mode != ProvenanceMode::Strict {
            trace!("Exposing allocation id {alloc_id:?}");
            global_state.exposed.insert(alloc_id);
            if ecx.machine.stacked_borrows.is_some() {
                ecx.expose_tag(alloc_id, sb);
            }
        }
    }

    pub fn ptr_from_addr_transmute(
        ecx: &MiriEvalContext<'mir, 'tcx>,
        addr: u64,
    ) -> Pointer<Option<Tag>> {
        trace!("Transmuting 0x{:x} to a pointer", addr);

        let provenance = if ecx.machine.allow_ptr_int_transmute {
            // When we allow transmutes, treat them like casts: generating a wildcard pointer.
            Some(Tag::Wildcard)
        } else {
            // Usually, we consider transmuted pointers to be "invalid" (`None` provenance).
            None
        };
        Pointer::new(provenance, Size::from_bytes(addr))
    }

    pub fn ptr_from_addr_cast(
        ecx: &MiriEvalContext<'mir, 'tcx>,
        addr: u64,
    ) -> InterpResult<'tcx, Pointer<Option<Tag>>> {
        trace!("Casting 0x{:x} to a pointer", addr);

        let global_state = ecx.machine.intptrcast.borrow();

        match global_state.provenance_mode {
            ProvenanceMode::Default => {
                // The first time this happens, print a warning.
                use std::sync::atomic::{AtomicBool, Ordering};
                static FIRST_WARNING: AtomicBool = AtomicBool::new(true);
                if FIRST_WARNING.swap(false, Ordering::Relaxed) {
                    register_diagnostic(NonHaltingDiagnostic::Int2Ptr);
                }
            }
            ProvenanceMode::Strict => {
                throw_unsup_format!(
                    "integer-to-pointer casts and `from_exposed_addr` are not supported with `-Zmiri-strict-provenance`; use `with_addr` instead"
                )
            }
            ProvenanceMode::Permissive => {}
        }

        // This is how wildcard pointers are born.
        Ok(Pointer::new(Some(Tag::Wildcard), Size::from_bytes(addr)))
    }

    fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 {
        let mut global_state = ecx.machine.intptrcast.borrow_mut();
        let global_state = &mut *global_state;

        match global_state.base_addr.entry(alloc_id) {
            Entry::Occupied(entry) => *entry.get(),
            Entry::Vacant(entry) => {
                // There is nothing wrong with a raw pointer being cast to an integer only after
                // it became dangling.  Hence `MaybeDead`.
                let (size, align) =
                    ecx.get_alloc_size_and_align(alloc_id, AllocCheck::MaybeDead).unwrap();

                // This allocation does not have a base address yet, pick one.
                // Leave some space to the previous allocation, to give it some chance to be less aligned.
                let slack = {
                    let mut rng = ecx.machine.rng.borrow_mut();
                    // This means that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
                    rng.gen_range(0..16)
                };
                // From next_base_addr + slack, round up to adjust for alignment.
                let base_addr = global_state.next_base_addr.checked_add(slack).unwrap();
                let base_addr = Self::align_addr(base_addr, align.bytes());
                entry.insert(base_addr);
                trace!(
                    "Assigning base address {:#x} to allocation {:?} (size: {}, align: {}, slack: {})",
                    base_addr,
                    alloc_id,
                    size.bytes(),
                    align.bytes(),
                    slack,
                );

                // Remember next base address.  Leave a gap of at least 1 to avoid two zero-sized allocations
                // having the same base address, and to avoid ambiguous provenance for the address between two
                // allocations (also see https://github.com/rust-lang/unsafe-code-guidelines/issues/313).
                let size_plus_1 = size.bytes().checked_add(1).unwrap();
                global_state.next_base_addr = base_addr.checked_add(size_plus_1).unwrap();
                // Given that `next_base_addr` increases in each allocation, pushing the
                // corresponding tuple keeps `int_to_ptr_map` sorted
                global_state.int_to_ptr_map.push((base_addr, alloc_id));

                base_addr
            }
        }
    }

    /// Convert a relative (tcx) pointer to an absolute address.
    pub fn rel_ptr_to_addr(ecx: &MiriEvalContext<'mir, 'tcx>, ptr: Pointer<AllocId>) -> u64 {
        let (alloc_id, offset) = ptr.into_parts(); // offset is relative (AllocId provenance)
        let base_addr = GlobalStateInner::alloc_base_addr(ecx, alloc_id);

        // Add offset with the right kind of pointer-overflowing arithmetic.
        let dl = ecx.data_layout();
        dl.overflowing_offset(base_addr, offset.bytes()).0
    }

    /// When a pointer is used for a memory access, this computes where in which allocation the
    /// access is going.
    pub fn abs_ptr_to_rel(
        ecx: &MiriEvalContext<'mir, 'tcx>,
        ptr: Pointer<Tag>,
    ) -> Option<(AllocId, Size)> {
        let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance)

        let alloc_id = if let Tag::Concrete { alloc_id, .. } = tag {
            alloc_id
        } else {
            // A wildcard pointer.
            GlobalStateInner::alloc_id_from_addr(ecx, addr.bytes())?
        };

        let base_addr = GlobalStateInner::alloc_base_addr(ecx, alloc_id);

        // Wrapping "addr - base_addr"
        let dl = ecx.data_layout();
        let neg_base_addr = (base_addr as i64).wrapping_neg();
        Some((
            alloc_id,
            Size::from_bytes(dl.overflowing_signed_offset(addr.bytes(), neg_base_addr).0),
        ))
    }

    /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
    /// of `align` that is larger or equal to `addr`
    fn align_addr(addr: u64, align: u64) -> u64 {
        match addr % align {
            0 => addr,
            rem => addr.checked_add(align).unwrap() - rem,
        }
    }
}

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

    #[test]
    fn test_align_addr() {
        assert_eq!(GlobalStateInner::align_addr(37, 4), 40);
        assert_eq!(GlobalStateInner::align_addr(44, 4), 44);
    }
}
<|endoftext|>


<commit_before>//!
//! The sort tool sorts JSON arrays, optionally using a supplied tool name
//!

use std::cmp::*;
use std::result::Result;
use serde_json::*;
use silkthread_base::*;

use super::compare::*;

///
/// Parameters that can be passed to the sort tool
///
#[derive(Serialize, Deserialize)]
pub struct SortParameters {
    /// Values to sort
    array: Vec<Value>,

    /// Tool to use to make comparisons
    compare_tool: Option<String>
}

impl SortParameters {
    pub fn new<'a>(array: Vec<Value>, compare_tool: Option<&'a str>) -> SortParameters {
        SortParameters { array: array, compare_tool: compare_tool.map(|s| String::from(s)) }
    }
}

///
/// Tool that can be used to sort JSON arrays
///
pub struct SortTool {
    /// Default comparison tool
    default_compare_tool: Box<Tool>
}

impl SortTool {
    ///
    /// Creates a new sort tool, using the standard comparison tool
    ///
    pub fn new() -> SortTool {
        SortTool { default_compare_tool: Box::new(CompareTool::new()) }
    }

    ///
    /// Sorts an array of JSON values
    ///
    pub fn sort(mut array: Vec<Value>, compare_tool: &Box<Tool>, environment: &Environment) -> Vec<Value> {
        // Sort the array using the comparison tool for ordering
        array.sort_by(|v1, v2| {
            // Perform the comparison
            // We ignore errors from the comparison tool
            let compare_result  = compare_tool.invoke_json(json![ [ v1, v2 ] ], environment).unwrap_or(Value::Number(Number::from_f64(0.0).unwrap()));
            let ordering        = match compare_result { Value::Number(ref n) => n.as_f64().unwrap_or(0.0), _ => 0.0 };

            // Tool should return a number indicating ordering
            // (For now, we assume it's indicating equality in the case where it gives an error or a value other than a number)
            if ordering < 0.0 {
                Ordering::Less
            } else if ordering > 0.0 {
                Ordering::Greater
            } else {
                Ordering::Equal
            }
        });

        array
    }

    ///
    /// Sorts an array of JSON values, using the default tool
    ///
    pub fn sort_default(&self, array: Vec<Value>, environment: &Environment) -> Vec<Value> {
        SortTool::sort(array, &self.default_compare_tool, environment)
    }
}

impl Tool for SortTool {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        if let Value::Object(sort_parameters) = input {
            if let Some(&Value::Array(ref array)) = sort_parameters.get("array") {
                // We either sort using the default ordering (ie, CompareTool) or a custom tool
                if let Some(&Value::String(ref compare_tool_name)) = sort_parameters.get("compare_tool") {
                    // Tool comes from the current environment
                    let compare_tool = environment.get_json_tool(&compare_tool_name);

                    // Sort if we successfully fetched a tool, error if we did not
                    match compare_tool {
                        Ok(compare_tool) => {
                            Ok(Value::Array(SortTool::sort(array.clone(), &compare_tool, environment)))
                        },

                        Err(retrieve_error) => {
                            Err(json![ {
                                "error":        "Compare tool not found",
                                "description":  retrieve_error.message()
                            } ])
                        }
                    }
                } else {
                    // No compare tool name supplied
                    Ok(Value::Array(SortTool::sort(array.clone(), &self.default_compare_tool, environment)))
                }
            } else {
                // Parameters are an object but there's no array
                Err(json![ {
                    "error": "Parameters to sort-tool must be an array or of the form { \"array\": <array>, \"compare_tool\": <tool_name> }"
                }])
            }
        } else if let Value::Array(array) = input {
            // Input is just an array
            Ok(Value::Array(self.sort_default(array, environment)))
        } else {
            Err(json![ {
                "error": "Parameters to sort-tool must be an array or of the form { \"array\": <array>, \"compare_tool\": <tool_name> }"
            }])
        }
    }
}

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

    #[test]
    fn can_sort_array() {
        let env     = EmptyEnvironment::new();
        let tool    = TypedTool::from(Box::new(SortTool::new()));

        assert!(tool.invoke(vec![ 2, 5, 3, 1, 4 ], &env) == Ok(vec![ 1,2,3,4,5 ]));
    }

    #[test]
    fn can_sort_using_object() {
        let env     = EmptyEnvironment::new();
        let tool    = TypedTool::from(Box::new(SortTool::new()));

        assert!(tool.invoke(SortParameters::new(vec![ json![2], json![5], json![3], json![1], json![4] ], None), &env) == Ok(vec![ 1,2,3,4,5 ]));
    }

    #[test]
    fn can_sort_using_custom_tool() {
        let env             = DynamicEnvironment::new();
        let tool            = TypedTool::from(Box::new(SortTool::new()));
        let compare_tool    = TypedTool::from(Box::new(CompareTool::new()));

        env.define("reverse-compare", Box::new(make_pure_tool(move |(a, b) : (Value, Value)| -> i32 {
            -compare_tool.invoke((a, b), &EmptyEnvironment::new()).unwrap_or(0)
        })));

        assert!(tool.invoke(SortParameters::new(vec![ json![2], json![5], json![3], json![1], json![4] ], Some("reverse-compare")), &env) == Ok(vec![ 5,4,3,2,1 ]));
    }

    #[test]
    fn missing_compare_tool_is_error() {
        let env = EmptyEnvironment::new();
        let tool: TypedTool<SortParameters, Vec<i32>> = TypedTool::from(Box::new(SortTool::new()));

        assert!(tool.invoke(SortParameters::new(vec![ json![2], json![5], json![3], json![1], json![4] ], Some("missing")), &env).is_err());
    }
}
<commit_msg>Rename test<commit_after>//!
//! The sort tool sorts JSON arrays, optionally using a supplied tool name
//!

use std::cmp::*;
use std::result::Result;
use serde_json::*;
use silkthread_base::*;

use super::compare::*;

///
/// Parameters that can be passed to the sort tool
///
#[derive(Serialize, Deserialize)]
pub struct SortParameters {
    /// Values to sort
    array: Vec<Value>,

    /// Tool to use to make comparisons
    compare_tool: Option<String>
}

impl SortParameters {
    pub fn new<'a>(array: Vec<Value>, compare_tool: Option<&'a str>) -> SortParameters {
        SortParameters { array: array, compare_tool: compare_tool.map(|s| String::from(s)) }
    }
}

///
/// Tool that can be used to sort JSON arrays
///
pub struct SortTool {
    /// Default comparison tool
    default_compare_tool: Box<Tool>
}

impl SortTool {
    ///
    /// Creates a new sort tool, using the standard comparison tool
    ///
    pub fn new() -> SortTool {
        SortTool { default_compare_tool: Box::new(CompareTool::new()) }
    }

    ///
    /// Sorts an array of JSON values
    ///
    pub fn sort(mut array: Vec<Value>, compare_tool: &Box<Tool>, environment: &Environment) -> Vec<Value> {
        // Sort the array using the comparison tool for ordering
        array.sort_by(|v1, v2| {
            // Perform the comparison
            // We ignore errors from the comparison tool
            let compare_result  = compare_tool.invoke_json(json![ [ v1, v2 ] ], environment).unwrap_or(Value::Number(Number::from_f64(0.0).unwrap()));
            let ordering        = match compare_result { Value::Number(ref n) => n.as_f64().unwrap_or(0.0), _ => 0.0 };

            // Tool should return a number indicating ordering
            // (For now, we assume it's indicating equality in the case where it gives an error or a value other than a number)
            if ordering < 0.0 {
                Ordering::Less
            } else if ordering > 0.0 {
                Ordering::Greater
            } else {
                Ordering::Equal
            }
        });

        array
    }

    ///
    /// Sorts an array of JSON values, using the default tool
    ///
    pub fn sort_default(&self, array: Vec<Value>, environment: &Environment) -> Vec<Value> {
        SortTool::sort(array, &self.default_compare_tool, environment)
    }
}

impl Tool for SortTool {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        if let Value::Object(sort_parameters) = input {
            if let Some(&Value::Array(ref array)) = sort_parameters.get("array") {
                // We either sort using the default ordering (ie, CompareTool) or a custom tool
                if let Some(&Value::String(ref compare_tool_name)) = sort_parameters.get("compare_tool") {
                    // Tool comes from the current environment
                    let compare_tool = environment.get_json_tool(&compare_tool_name);

                    // Sort if we successfully fetched a tool, error if we did not
                    match compare_tool {
                        Ok(compare_tool) => {
                            Ok(Value::Array(SortTool::sort(array.clone(), &compare_tool, environment)))
                        },

                        Err(retrieve_error) => {
                            Err(json![ {
                                "error":        "Compare tool not found",
                                "description":  retrieve_error.message()
                            } ])
                        }
                    }
                } else {
                    // No compare tool name supplied
                    Ok(Value::Array(SortTool::sort(array.clone(), &self.default_compare_tool, environment)))
                }
            } else {
                // Parameters are an object but there's no array
                Err(json![ {
                    "error": "Parameters to sort-tool must be an array or of the form { \"array\": <array>, \"compare_tool\": <tool_name> }"
                }])
            }
        } else if let Value::Array(array) = input {
            // Input is just an array
            Ok(Value::Array(self.sort_default(array, environment)))
        } else {
            Err(json![ {
                "error": "Parameters to sort-tool must be an array or of the form { \"array\": <array>, \"compare_tool\": <tool_name> }"
            }])
        }
    }
}

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

    #[test]
    fn can_sort_array() {
        let env     = EmptyEnvironment::new();
        let tool    = TypedTool::from(Box::new(SortTool::new()));

        assert!(tool.invoke(vec![ 2, 5, 3, 1, 4 ], &env) == Ok(vec![ 1,2,3,4,5 ]));
    }

    #[test]
    fn can_sort_using_parameter_object() {
        let env     = EmptyEnvironment::new();
        let tool    = TypedTool::from(Box::new(SortTool::new()));

        assert!(tool.invoke(SortParameters::new(vec![ json![2], json![5], json![3], json![1], json![4] ], None), &env) == Ok(vec![ 1,2,3,4,5 ]));
    }

    #[test]
    fn can_sort_using_custom_tool() {
        let env             = DynamicEnvironment::new();
        let tool            = TypedTool::from(Box::new(SortTool::new()));
        let compare_tool    = TypedTool::from(Box::new(CompareTool::new()));

        env.define("reverse-compare", Box::new(make_pure_tool(move |(a, b) : (Value, Value)| -> i32 {
            -compare_tool.invoke((a, b), &EmptyEnvironment::new()).unwrap_or(0)
        })));

        assert!(tool.invoke(SortParameters::new(vec![ json![2], json![5], json![3], json![1], json![4] ], Some("reverse-compare")), &env) == Ok(vec![ 5,4,3,2,1 ]));
    }

    #[test]
    fn missing_compare_tool_is_error() {
        let env = EmptyEnvironment::new();
        let tool: TypedTool<SortParameters, Vec<i32>> = TypedTool::from(Box::new(SortTool::new()));

        assert!(tool.invoke(SortParameters::new(vec![ json![2], json![5], json![3], json![1], json![4] ], Some("missing")), &env).is_err());
    }
}
<|endoftext|>


<commit_before>//! Lang item required to make the normal `main` work in applications
//!
//! This is how the `start` lang item works:
//! When `rustc` compiles a binary crate, it creates a `main` function that looks
//! like this:
//!
//! ```
//! #[export_name = "main"]
//! pub extern "C" fn rustc_main(argc: isize, argv: *const *const u8) -> isize {
//!     start(main, argc, argv)
//! }
//! ```
//!
//! Where `start` is this function and `main` is the binary crate's `main`
//! function.
//!
//! The final piece is that the entry point of our program, _start, has to call
//! `rustc_main`. That's covered by the `_start` function in the root of this
//! crate.

use crate::led::LedDriver;
use crate::timer::Duration;
use crate::timer::ParallelSleepDriver;
use crate::Drivers;
use core::alloc::Layout;
use core::executor;
use core::panic::PanicInfo;

#[lang = "start"]
extern "C" fn start<T>(main: fn() -> T, _argc: isize, _argv: *const *const u8)
where
    T: Termination,
{
    main();
}

#[lang = "termination"]
pub trait Termination {}

impl Termination for () {}

impl Termination for crate::result::TockResult<()> {}

#[panic_handler]
unsafe fn panic_handler(_info: &PanicInfo) -> ! {
    // Signal a panic using the LowLevelDebug capsule (if available).
    super::debug::low_level_status_code(1);

    // Flash all LEDs (if available).
    executor::block_on(async {
        let Drivers {
            led_driver_factory,
            timer_context,
            ..
        } = crate::retrieve_drivers_unsafe();
        let mut driver = timer_context.create_timer_driver();
        let timer_driver = driver.activate().ok();
        let led_driver = led_driver_factory.create_driver().ok();
        if let (Some(ref led_driver), Some(ref timer_driver)) = (led_driver, timer_driver) {
            blink_all_leds(timer_driver, led_driver).await;
        }
        loop {}
    });
    // Never type is not supported for T in Future
    unreachable!()
}

async fn blink_all_leds(timer_driver: &ParallelSleepDriver<'_>, led_driver: &LedDriver) {
    loop {
        for led in led_driver.all() {
            let _ = led.on();
        }
        let _ = timer_driver.sleep(Duration::from_ms(100)).await;
        for led in led_driver.all() {
            let _ = led.off();
        }
        let _ = timer_driver.sleep(Duration::from_ms(100)).await;
    }
}

#[alloc_error_handler]
unsafe fn alloc_error_handler(_: Layout) -> ! {
    executor::block_on(async {
        let Drivers {
            led_driver_factory,
            timer_context,
            ..
        } = crate::retrieve_drivers_unsafe();
        let mut driver = timer_context.create_timer_driver();
        let timer_driver = driver.activate().ok();
        let led_driver = led_driver_factory.create_driver().ok();

        if let (Some(led_driver), Some(timer_driver)) = (led_driver, timer_driver) {
            cycle_all_leds(&timer_driver, &led_driver).await;
        }
        loop {}
    });
    // Never type is not supported for T in Future
    unreachable!()
}

async fn cycle_all_leds(timer_driver: &ParallelSleepDriver<'_>, led_driver: &LedDriver) {
    loop {
        for led in led_driver.all() {
            let _ = led.on();
        }
        let _ = timer_driver.sleep(Duration::from_ms(100)).await;
        for led in led_driver.all() {
            let _ = led.off();
        }
        let _ = timer_driver.sleep(Duration::from_ms(100)).await;
    }
}
<commit_msg>Fix cycle_all_leds method<commit_after>//! Lang item required to make the normal `main` work in applications
//!
//! This is how the `start` lang item works:
//! When `rustc` compiles a binary crate, it creates a `main` function that looks
//! like this:
//!
//! ```
//! #[export_name = "main"]
//! pub extern "C" fn rustc_main(argc: isize, argv: *const *const u8) -> isize {
//!     start(main, argc, argv)
//! }
//! ```
//!
//! Where `start` is this function and `main` is the binary crate's `main`
//! function.
//!
//! The final piece is that the entry point of our program, _start, has to call
//! `rustc_main`. That's covered by the `_start` function in the root of this
//! crate.

use crate::led::LedDriver;
use crate::timer::Duration;
use crate::timer::ParallelSleepDriver;
use crate::Drivers;
use core::alloc::Layout;
use core::executor;
use core::panic::PanicInfo;

#[lang = "start"]
extern "C" fn start<T>(main: fn() -> T, _argc: isize, _argv: *const *const u8)
where
    T: Termination,
{
    main();
}

#[lang = "termination"]
pub trait Termination {}

impl Termination for () {}

impl Termination for crate::result::TockResult<()> {}

#[panic_handler]
unsafe fn panic_handler(_info: &PanicInfo) -> ! {
    // Signal a panic using the LowLevelDebug capsule (if available).
    super::debug::low_level_status_code(1);

    // Flash all LEDs (if available).
    executor::block_on(async {
        let Drivers {
            led_driver_factory,
            timer_context,
            ..
        } = crate::retrieve_drivers_unsafe();
        let mut driver = timer_context.create_timer_driver();
        let timer_driver = driver.activate().ok();
        let led_driver = led_driver_factory.create_driver().ok();
        if let (Some(ref led_driver), Some(ref timer_driver)) = (led_driver, timer_driver) {
            blink_all_leds(timer_driver, led_driver).await;
        }
        loop {}
    });
    // Never type is not supported for T in Future
    unreachable!()
}

async fn blink_all_leds(timer_driver: &ParallelSleepDriver<'_>, led_driver: &LedDriver) {
    loop {
        for led in led_driver.all() {
            let _ = led.on();
        }
        let _ = timer_driver.sleep(Duration::from_ms(100)).await;
        for led in led_driver.all() {
            let _ = led.off();
        }
        let _ = timer_driver.sleep(Duration::from_ms(100)).await;
    }
}

#[alloc_error_handler]
unsafe fn alloc_error_handler(_: Layout) -> ! {
    executor::block_on(async {
        let Drivers {
            led_driver_factory,
            timer_context,
            ..
        } = crate::retrieve_drivers_unsafe();
        let mut driver = timer_context.create_timer_driver();
        let timer_driver = driver.activate().ok();
        let led_driver = led_driver_factory.create_driver().ok();

        if let (Some(led_driver), Some(timer_driver)) = (led_driver, timer_driver) {
            cycle_all_leds(&timer_driver, &led_driver).await;
        }
        loop {}
    });
    // Never type is not supported for T in Future
    unreachable!()
}

async fn cycle_all_leds(timer_driver: &ParallelSleepDriver<'_>, led_driver: &LedDriver) {
    loop {
        for led in led_driver.all() {
            let _ = led.on();
            let _ = timer_driver.sleep(Duration::from_ms(100)).await;
            let _ = led.off();
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin Developers
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// Grin server commands processing
use std::env::current_dir;
use std::process::exit;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use clap::ArgMatches;
use ctrlc;
use daemonize::Daemonize;

use config::GlobalConfig;
use core::global;
use p2p::Seeding;
use servers;
use tui::ui;
use util::LOGGER;

/// wrap below to allow UI to clean up on stop
fn start_server(config: servers::ServerConfig) {
	start_server_tui(config);
	// Just kill process for now, otherwise the process
	// hangs around until sigint because the API server
	// currently has no shutdown facility
	println!("Shutting down...");
	thread::sleep(Duration::from_millis(1000));
	println!("Shutdown complete.");
	exit(0);
}

fn start_server_tui(config: servers::ServerConfig) {
	// Run the UI controller.. here for now for simplicity to access
	// everything it might need
	if config.run_tui.is_some() && config.run_tui.unwrap() {
		println!("Starting GRIN in UI mode...");
		servers::Server::start(config, |serv: Arc<servers::Server>| {
			let running = Arc::new(AtomicBool::new(true));
			let r = running.clone();
			let _ = thread::Builder::new()
				.name("ui".to_string())
				.spawn(move || {
					let mut controller = ui::Controller::new().unwrap_or_else(|e| {
						panic!("Error loading UI controller: {}", e);
					});
					controller.run(serv.clone(), r);
				});
			ctrlc::set_handler(move || {
				running.store(false, Ordering::SeqCst);
			}).expect("Error setting Ctrl-C handler");
		}).unwrap();
	} else {
		servers::Server::start(config, |serv: Arc<servers::Server>| {
			let running = Arc::new(AtomicBool::new(true));
			let r = running.clone();
			ctrlc::set_handler(move || {
				r.store(false, Ordering::SeqCst);
			}).expect("Error setting Ctrl-C handler");
			while running.load(Ordering::SeqCst) {
				thread::sleep(Duration::from_secs(1));
			}
			warn!(LOGGER, "Received SIGINT (Ctrl+C).");
			serv.stop();
		}).unwrap();
	}
}

/// Handles the server part of the command line, mostly running, starting and
/// stopping the Grin blockchain server. Processes all the command line
/// arguments to build a proper configuration and runs Grin with that
/// configuration.
pub fn server_command(server_args: Option<&ArgMatches>, mut global_config: GlobalConfig) {
	global::set_mining_mode(
		global_config
			.members
			.as_mut()
			.unwrap()
			.server
			.clone()
			.chain_type,
	);

	// just get defaults from the global config
	let mut server_config = global_config.members.as_ref().unwrap().server.clone();

	if let Some(a) = server_args {
		if let Some(port) = a.value_of("port") {
			server_config.p2p_config.port = port.parse().unwrap();
		}

		if let Some(api_port) = a.value_of("api_port") {
			let default_ip = "0.0.0.0";
			server_config.api_http_addr = format!("{}:{}", default_ip, api_port);
		}

		if let Some(wallet_url) = a.value_of("wallet_url") {
			server_config
				.stratum_mining_config
				.as_mut()
				.unwrap()
				.wallet_listener_url = wallet_url.to_string();
		}

		if let Some(seeds) = a.values_of("seed") {
			server_config.p2p_config.seeding_type = Seeding::List;
			server_config.p2p_config.seeds = Some(seeds.map(|s| s.to_string()).collect());
		}
	}

	/*if let Some(true) = server_config.run_wallet_listener {
		let mut wallet_config = global_config.members.as_ref().unwrap().wallet.clone();
		wallet::init_wallet_seed(wallet_config.clone());
		let wallet = wallet::instantiate_wallet(wallet_config.clone(), "");

		let _ = thread::Builder::new()
			.name("wallet_listener".to_string())
			.spawn(move || {
				controller::foreign_listener(wallet, &wallet_config.api_listen_addr())
					.unwrap_or_else(|e| {
						panic!(
							"Error creating wallet listener: {:?} Config: {:?}",
							e, wallet_config
						)
					});
			});
	}
	if let Some(true) = server_config.run_wallet_owner_api {
		let mut wallet_config = global_config.members.unwrap().wallet;
		let wallet = wallet::instantiate_wallet(wallet_config.clone(), "");
		wallet::init_wallet_seed(wallet_config.clone());

		let _ = thread::Builder::new()
			.name("wallet_owner_listener".to_string())
			.spawn(move || {
				controller::owner_listener(wallet, "127.0.0.1:13420").unwrap_or_else(|e| {
					panic!(
						"Error creating wallet api listener: {:?} Config: {:?}",
						e, wallet_config
					)
				});
			});
	}*/

	// start the server in the different run modes (interactive or daemon)
	if let Some(a) = server_args {
		match a.subcommand() {
			("run", _) => {
				start_server(server_config);
			}
			("start", _) => {
				let daemonize = Daemonize::new()
					.pid_file("/tmp/grin.pid")
					.chown_pid_file(true)
					.working_directory(current_dir().unwrap())
					.privileged_action(move || {
						start_server(server_config.clone());
						loop {
							thread::sleep(Duration::from_secs(60));
						}
					});
				match daemonize.start() {
					Ok(_) => info!(LOGGER, "Grin server successfully started."),
					Err(e) => error!(LOGGER, "Error starting: {}", e),
				}
			}
			("stop", _) => println!("TODO. Just 'kill $pid' for now. Maybe /tmp/grin.pid is $pid"),
			(cmd, _) => {
				println!(":: {:?}", server_args);
				panic!(
					"Unknown server command '{}', use 'grin help server' for details",
					cmd
				);
			}
		}
	} else {
		start_server(server_config);
	}
}
<commit_msg>Small cleanup of SIGINT and SIGTERM handler (#1535)<commit_after>// Copyright 2018 The Grin Developers
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// Grin server commands processing
use std::env::current_dir;
use std::process::exit;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use clap::ArgMatches;
use ctrlc;
use daemonize::Daemonize;

use config::GlobalConfig;
use core::global;
use p2p::Seeding;
use servers;
use tui::ui;
use util::LOGGER;

/// wrap below to allow UI to clean up on stop
fn start_server(config: servers::ServerConfig) {
	start_server_tui(config);
	// Just kill process for now, otherwise the process
	// hangs around until sigint because the API server
	// currently has no shutdown facility
	warn!(LOGGER, "Shutting down...");
	thread::sleep(Duration::from_millis(1000));
	warn!(LOGGER, "Shutdown complete.");
	exit(0);
}

fn start_server_tui(config: servers::ServerConfig) {
	// Run the UI controller.. here for now for simplicity to access
	// everything it might need
	if config.run_tui.is_some() && config.run_tui.unwrap() {
		warn!(LOGGER, "Starting GRIN in UI mode...");
		servers::Server::start(config, |serv: Arc<servers::Server>| {
			let running = Arc::new(AtomicBool::new(true));
			let _ = thread::Builder::new()
				.name("ui".to_string())
				.spawn(move || {
					let mut controller = ui::Controller::new().unwrap_or_else(|e| {
						panic!("Error loading UI controller: {}", e);
					});
					controller.run(serv.clone(), running);
				});
		}).unwrap();
	} else {
		warn!(LOGGER, "Starting GRIN w/o UI...");
		servers::Server::start(config, |serv: Arc<servers::Server>| {
			let running = Arc::new(AtomicBool::new(true));
			let r = running.clone();
			ctrlc::set_handler(move || {
				r.store(false, Ordering::SeqCst);
			}).expect("Error setting handler for both SIGINT (Ctrl+C) and SIGTERM (kill)");
			while running.load(Ordering::SeqCst) {
				thread::sleep(Duration::from_secs(1));
			}
			warn!(LOGGER, "Received SIGINT (Ctrl+C) or SIGTERM (kill).");
			serv.stop();
		}).unwrap();
	}
}

/// Handles the server part of the command line, mostly running, starting and
/// stopping the Grin blockchain server. Processes all the command line
/// arguments to build a proper configuration and runs Grin with that
/// configuration.
pub fn server_command(server_args: Option<&ArgMatches>, mut global_config: GlobalConfig) {
	global::set_mining_mode(
		global_config
			.members
			.as_mut()
			.unwrap()
			.server
			.clone()
			.chain_type,
	);

	// just get defaults from the global config
	let mut server_config = global_config.members.as_ref().unwrap().server.clone();

	if let Some(a) = server_args {
		if let Some(port) = a.value_of("port") {
			server_config.p2p_config.port = port.parse().unwrap();
		}

		if let Some(api_port) = a.value_of("api_port") {
			let default_ip = "0.0.0.0";
			server_config.api_http_addr = format!("{}:{}", default_ip, api_port);
		}

		if let Some(wallet_url) = a.value_of("wallet_url") {
			server_config
				.stratum_mining_config
				.as_mut()
				.unwrap()
				.wallet_listener_url = wallet_url.to_string();
		}

		if let Some(seeds) = a.values_of("seed") {
			server_config.p2p_config.seeding_type = Seeding::List;
			server_config.p2p_config.seeds = Some(seeds.map(|s| s.to_string()).collect());
		}
	}

	/*if let Some(true) = server_config.run_wallet_listener {
		let mut wallet_config = global_config.members.as_ref().unwrap().wallet.clone();
		wallet::init_wallet_seed(wallet_config.clone());
		let wallet = wallet::instantiate_wallet(wallet_config.clone(), "");

		let _ = thread::Builder::new()
			.name("wallet_listener".to_string())
			.spawn(move || {
				controller::foreign_listener(wallet, &wallet_config.api_listen_addr())
					.unwrap_or_else(|e| {
						panic!(
							"Error creating wallet listener: {:?} Config: {:?}",
							e, wallet_config
						)
					});
			});
	}
	if let Some(true) = server_config.run_wallet_owner_api {
		let mut wallet_config = global_config.members.unwrap().wallet;
		let wallet = wallet::instantiate_wallet(wallet_config.clone(), "");
		wallet::init_wallet_seed(wallet_config.clone());

		let _ = thread::Builder::new()
			.name("wallet_owner_listener".to_string())
			.spawn(move || {
				controller::owner_listener(wallet, "127.0.0.1:13420").unwrap_or_else(|e| {
					panic!(
						"Error creating wallet api listener: {:?} Config: {:?}",
						e, wallet_config
					)
				});
			});
	}*/

	// start the server in the different run modes (interactive or daemon)
	if let Some(a) = server_args {
		match a.subcommand() {
			("run", _) => {
				start_server(server_config);
			}
			("start", _) => {
				let daemonize = Daemonize::new()
					.pid_file("/tmp/grin.pid")
					.chown_pid_file(true)
					.working_directory(current_dir().unwrap())
					.privileged_action(move || {
						start_server(server_config.clone());
						loop {
							thread::sleep(Duration::from_secs(60));
						}
					});
				match daemonize.start() {
					Ok(_) => info!(LOGGER, "Grin server successfully started."),
					Err(e) => error!(LOGGER, "Error starting: {}", e),
				}
			}
			("stop", _) => println!("TODO. Just 'kill $pid' for now. Maybe /tmp/grin.pid is $pid"),
			(cmd, _) => {
				println!(":: {:?}", server_args);
				panic!(
					"Unknown server command '{}', use 'grin help server' for details",
					cmd
				);
			}
		}
	} else {
		start_server(server_config);
	}
}
<|endoftext|>


<commit_before>use std::process::Command;
use std::fs::remove_dir_all;
use std::rc::Rc;

use paths::Paths;
use reporter::{report, report_command};

pub struct Downloader {
   paths: Rc<Paths>,
}

impl Downloader {
   pub fn new(paths: Rc<Paths>) -> Self {
      Downloader {
         paths: paths,
      }
   }

   pub fn download(&self) {
      if self.paths.already_downloaded() {
         report("Downloading", format!("nginx downloaded already - {}", self.paths.archive));
      } else {
         self.download_with_curl();
      }

      if self.paths.already_extracted() {
         self.remove_existing_extract_path();
      }

      self.extract();
   }

   fn download_with_curl(&self) {
      let args = vec!["-s", "-L", "-O", self.paths.http_location.as_slice()];

      report_command("Downloading", "curl", &args);

      Command::new("curl").args(&args).current_dir(&self.paths.target_path).output().unwrap_or_else(|e| {
         panic!("Downloading nginx with Curl failed: {}.", e)
      });
   }

   fn extract(&self) {
      let args = vec!["xzf", self.paths.archive.as_slice()];

      report_command("Extracting", "tar", &args);

      Command::new("tar").args(&args).current_dir(&self.paths.target_path).output().unwrap_or_else(|e| {
         panic!("Extracting nginx failed: {}.", e)
      });
   }

   fn remove_existing_extract_path(&self) {
      report("Removing", format!("previously extracted archive {:?}", &self.paths.extract_path));

      remove_dir_all(&self.paths.extract_path).unwrap_or_else(|e| {
         panic!("Cannot delete nginx extract path - {:?}: {}.", self.paths.extract_path, e)
      });
   }
}
<commit_msg>Deleting message change.<commit_after>use std::process::Command;
use std::fs::remove_dir_all;
use std::rc::Rc;

use paths::Paths;
use reporter::{report, report_command};

pub struct Downloader {
   paths: Rc<Paths>,
}

impl Downloader {
   pub fn new(paths: Rc<Paths>) -> Self {
      Downloader {
         paths: paths,
      }
   }

   pub fn download(&self) {
      if self.paths.already_downloaded() {
         report("Downloading", format!("nginx downloaded already - {}", self.paths.archive));
      } else {
         self.download_with_curl();
      }

      if self.paths.already_extracted() {
         self.remove_existing_extract_path();
      }

      self.extract();
   }

   fn download_with_curl(&self) {
      let args = vec!["-s", "-L", "-O", self.paths.http_location.as_slice()];

      report_command("Downloading", "curl", &args);

      Command::new("curl").args(&args).current_dir(&self.paths.target_path).output().unwrap_or_else(|e| {
         panic!("Downloading nginx with Curl failed: {}.", e)
      });
   }

   fn extract(&self) {
      let args = vec!["xzf", self.paths.archive.as_slice()];

      report_command("Extracting", "tar", &args);

      Command::new("tar").args(&args).current_dir(&self.paths.target_path).output().unwrap_or_else(|e| {
         panic!("Extracting nginx failed: {}.", e)
      });
   }

   fn remove_existing_extract_path(&self) {
      report("Deleting", format!("previously extracted {:?}", &self.paths.extract_path));

      remove_dir_all(&self.paths.extract_path).unwrap_or_else(|e| {
         panic!("Cannot delete nginx extract path - {:?}: {}.", self.paths.extract_path, e)
      });
   }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin Developers
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Basic status view definition

use chrono::prelude::Utc;
use cursive::direction::Orientation;
use cursive::traits::Identifiable;
use cursive::view::View;
use cursive::views::{BoxView, LinearLayout, TextView};
use cursive::Cursive;

use crate::tui::constants::VIEW_BASIC_STATUS;
use crate::tui::types::TUIStatusListener;

use crate::servers::common::types::SyncStatus;
use crate::servers::ServerStats;

const NANO_TO_MILLIS: f64 = 1.0 / 1_000_000.0;

pub struct TUIStatusView;

impl TUIStatusListener for TUIStatusView {
	/// Create basic status view
	fn create() -> Box<dyn View> {
		let basic_status_view = BoxView::with_full_screen(
			LinearLayout::new(Orientation::Vertical)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Current Status:               "))
						.child(TextView::new("Starting").with_id("basic_current_status")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Connected Peers:              "))
						.child(TextView::new("0").with_id("connected_peers")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal).child(TextView::new(
						"------------------------------------------------",
					)),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Header Tip Hash:              "))
						.child(TextView::new("  ").with_id("basic_header_tip_hash")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Header Chain Height:          "))
						.child(TextView::new("  ").with_id("basic_header_chain_height")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Header Cumulative Difficulty: "))
						.child(TextView::new("  ").with_id("basic_header_total_difficulty")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal).child(TextView::new(
						"------------------------------------------------",
					)),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Chain Tip Hash:               "))
						.child(TextView::new("  ").with_id("tip_hash")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Chain Height:                 "))
						.child(TextView::new("  ").with_id("chain_height")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Chain Cumulative Difficulty:  "))
						.child(TextView::new("  ").with_id("basic_total_difficulty")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal).child(TextView::new(
						"------------------------------------------------",
					)),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("  ").with_id("basic_mining_config_status")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("  ").with_id("basic_mining_status")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("  ").with_id("basic_network_info")),
				), //.child(logo_view)
		);
		Box::new(basic_status_view.with_id(VIEW_BASIC_STATUS))
	}

	/// update
	fn update(c: &mut Cursive, stats: &ServerStats) {
		//find and update here as needed
		let basic_status = {
			match stats.sync_status {
				SyncStatus::Initial => "Initializing".to_string(),
				SyncStatus::NoSync => "Running".to_string(),
				SyncStatus::AwaitingPeers(_) => "Waiting for peers".to_string(),
				SyncStatus::HeaderSync {
					current_height,
					highest_height,
				} => {
					let percent = if highest_height == 0 {
						0
					} else {
						current_height * 100 / highest_height
					};
					format!("Downloading headers: {}%, step 1/4", percent)
				}
				SyncStatus::TxHashsetDownload {
					start_time,
					downloaded_size,
					total_size,
				} => {
					if total_size > 0 {
						let percent = if total_size > 0 {
							downloaded_size * 100 / total_size
						} else {
							0
						};
						let start = start_time.timestamp_nanos();
						let fin = Utc::now().timestamp_nanos();
						let dur_ms = (fin - start) as f64 * NANO_TO_MILLIS;

						format!("Downloading {}(MB) chain state for state sync: {}% at {:.1?}(kB/s), step 2/4",
						total_size / 1_000_000,
						percent,
						if dur_ms > 1.0f64 { downloaded_size as f64 / dur_ms as f64 } else { 0f64 },
						)
					} else {
						let start = start_time.timestamp_millis();
						let fin = Utc::now().timestamp_millis();
						let dur_secs = (fin - start) / 1000;

						format!("Downloading chain state for state sync. Waiting remote peer to start: {}s, step 2/4",
										dur_secs,
										)
					}
				}
				SyncStatus::TxHashsetSetup => {
					"Preparing chain state for validation, step 3/4".to_string()
				}
				SyncStatus::TxHashsetValidation {
					kernels,
					kernel_total,
					rproofs,
					rproof_total,
				} => {
					// 10% of overall progress is attributed to kernel validation
					// 90% to range proofs (which are much longer)
					let mut percent = if kernel_total > 0 {
						kernels * 10 / kernel_total
					} else {
						0
					};
					percent += if rproof_total > 0 {
						rproofs * 90 / rproof_total
					} else {
						0
					};
					format!("Validating chain state: {}%, step 3/4", percent)
				}
				SyncStatus::TxHashsetSave => {
					"Finalizing chain state for state sync, step 3/4".to_string()
				}
				SyncStatus::TxHashsetDone => {
					"Finalized chain state for state sync, step 3/4".to_string()
				}
				SyncStatus::BodySync {
					current_height,
					highest_height,
				} => {
					let percent = if highest_height == 0 {
						0
					} else {
						current_height * 100 / highest_height
					};
					format!("Downloading blocks: {}%, step 4/4", percent)
				}
			}
		};
		/*let basic_mining_config_status = {
			if stats.mining_stats.is_enabled {
				"Configured as mining node"
			} else {
				"Configured as validating node only (not mining)"
			}
		};
		let (basic_mining_status, basic_network_info) = {
			if stats.mining_stats.is_enabled {
				if stats.is_syncing {
					(
						"Mining Status: Paused while syncing".to_string(),
						" ".to_string(),
					)
				} else if stats.mining_stats.combined_gps == 0.0 {
					(
						"Mining Status: Starting miner and awaiting first solution...".to_string(),
						" ".to_string(),
					)
				} else {
					(
						format!(
							"Mining Status: Mining at height {} at {:.*} GPS",
							stats.mining_stats.block_height, 4, stats.mining_stats.combined_gps
						),
						format!(
							"Cuckoo {} - Network Difficulty {}",
							stats.mining_stats.edge_bits,
							stats.mining_stats.network_difficulty.to_string()
						),
					)
				}
			} else {
				(" ".to_string(), " ".to_string())
			}
		};*/
		c.call_on_id("basic_current_status", |t: &mut TextView| {
			t.set_content(basic_status);
		});
		c.call_on_id("connected_peers", |t: &mut TextView| {
			t.set_content(stats.peer_count.to_string());
		});
		c.call_on_id("tip_hash", |t: &mut TextView| {
			t.set_content(stats.head.last_block_h.to_string());
		});
		c.call_on_id("chain_height", |t: &mut TextView| {
			t.set_content(stats.head.height.to_string());
		});
		c.call_on_id("basic_total_difficulty", |t: &mut TextView| {
			t.set_content(stats.head.total_difficulty.to_string());
		});
		c.call_on_id("basic_header_tip_hash", |t: &mut TextView| {
			t.set_content(stats.header_head.last_block_h.to_string());
		});
		c.call_on_id("basic_header_chain_height", |t: &mut TextView| {
			t.set_content(stats.header_head.height.to_string());
		});
		c.call_on_id("basic_header_total_difficulty", |t: &mut TextView| {
			t.set_content(stats.header_head.total_difficulty.to_string());
		});
		/*c.call_on_id("basic_mining_config_status", |t: &mut TextView| {
			t.set_content(basic_mining_config_status);
		});
		c.call_on_id("basic_mining_status", |t: &mut TextView| {
			t.set_content(basic_mining_status);
		});
		c.call_on_id("basic_network_info", |t: &mut TextView| {
			t.set_content(basic_network_info);
		});*/
	}
}
<commit_msg>Move 3 dots to the TUI<commit_after>// Copyright 2018 The Grin Developers
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Basic status view definition

use chrono::prelude::Utc;
use cursive::direction::Orientation;
use cursive::traits::Identifiable;
use cursive::view::View;
use cursive::views::{BoxView, LinearLayout, TextView};
use cursive::Cursive;

use crate::tui::constants::VIEW_BASIC_STATUS;
use crate::tui::types::TUIStatusListener;

use crate::servers::common::types::SyncStatus;
use crate::servers::ServerStats;

const NANO_TO_MILLIS: f64 = 1.0 / 1_000_000.0;

pub struct TUIStatusView;

impl TUIStatusListener for TUIStatusView {
	/// Create basic status view
	fn create() -> Box<dyn View> {
		let basic_status_view = BoxView::with_full_screen(
			LinearLayout::new(Orientation::Vertical)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Current Status:               "))
						.child(TextView::new("Starting").with_id("basic_current_status")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Connected Peers:              "))
						.child(TextView::new("0").with_id("connected_peers")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal).child(TextView::new(
						"------------------------------------------------",
					)),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Header Tip Hash:              "))
						.child(TextView::new("  ").with_id("basic_header_tip_hash")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Header Chain Height:          "))
						.child(TextView::new("  ").with_id("basic_header_chain_height")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Header Cumulative Difficulty: "))
						.child(TextView::new("  ").with_id("basic_header_total_difficulty")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal).child(TextView::new(
						"------------------------------------------------",
					)),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Chain Tip Hash:               "))
						.child(TextView::new("  ").with_id("tip_hash")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Chain Height:                 "))
						.child(TextView::new("  ").with_id("chain_height")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("Chain Cumulative Difficulty:  "))
						.child(TextView::new("  ").with_id("basic_total_difficulty")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal).child(TextView::new(
						"------------------------------------------------",
					)),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("  ").with_id("basic_mining_config_status")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("  ").with_id("basic_mining_status")),
				)
				.child(
					LinearLayout::new(Orientation::Horizontal)
						.child(TextView::new("  ").with_id("basic_network_info")),
				), //.child(logo_view)
		);
		Box::new(basic_status_view.with_id(VIEW_BASIC_STATUS))
	}

	/// update
	fn update(c: &mut Cursive, stats: &ServerStats) {
		//find and update here as needed
		let basic_status = {
			match stats.sync_status {
				SyncStatus::Initial => "Initializing".to_string(),
				SyncStatus::NoSync => "Running".to_string(),
				SyncStatus::AwaitingPeers(_) => "Waiting for peers".to_string(),
				SyncStatus::HeaderSync {
					current_height,
					highest_height,
				} => {
					let percent = if highest_height == 0 {
						0
					} else {
						current_height * 100 / highest_height
					};
					format!("Downloading headers: {}%, step 1/4", percent)
				}
				SyncStatus::TxHashsetDownload {
					start_time,
					downloaded_size,
					total_size,
				} => {
					if total_size > 0 {
						let percent = if total_size > 0 {
							downloaded_size * 100 / total_size
						} else {
							0
						};
						let start = start_time.timestamp_nanos();
						let fin = Utc::now().timestamp_nanos();
						let dur_ms = (fin - start) as f64 * NANO_TO_MILLIS;

						format!("Downloading {}(MB) chain state for state sync: {}% at {:.1?}(kB/s), step 2/4",
						total_size / 1_000_000,
						percent,
						if dur_ms > 1.0f64 { downloaded_size as f64 / dur_ms as f64 } else { 0f64 },
						)
					} else {
						let start = start_time.timestamp_millis();
						let fin = Utc::now().timestamp_millis();
						let dur_secs = (fin - start) / 1000;

						format!("Downloading chain state for state sync. Waiting remote peer to start: {}s, step 2/4",
										dur_secs,
										)
					}
				}
				SyncStatus::TxHashsetSetup => {
					"Preparing chain state for validation, step 3/4".to_string()
				}
				SyncStatus::TxHashsetValidation {
					kernels,
					kernel_total,
					rproofs,
					rproof_total,
				} => {
					// 10% of overall progress is attributed to kernel validation
					// 90% to range proofs (which are much longer)
					let mut percent = if kernel_total > 0 {
						kernels * 10 / kernel_total
					} else {
						0
					};
					percent += if rproof_total > 0 {
						rproofs * 90 / rproof_total
					} else {
						0
					};
					format!("Validating chain state: {}%, step 3/4", percent)
				}
				SyncStatus::TxHashsetSave => {
					"Finalizing chain state for state sync, step 3/4".to_string()
				}
				SyncStatus::TxHashsetDone => {
					"Finalized chain state for state sync, step 3/4".to_string()
				}
				SyncStatus::BodySync {
					current_height,
					highest_height,
				} => {
					let percent = if highest_height == 0 {
						0
					} else {
						current_height * 100 / highest_height
					};
					format!("Downloading blocks: {}%, step 4/4", percent)
				}
			}
		};
		/*let basic_mining_config_status = {
			if stats.mining_stats.is_enabled {
				"Configured as mining node"
			} else {
				"Configured as validating node only (not mining)"
			}
		};
		let (basic_mining_status, basic_network_info) = {
			if stats.mining_stats.is_enabled {
				if stats.is_syncing {
					(
						"Mining Status: Paused while syncing".to_string(),
						" ".to_string(),
					)
				} else if stats.mining_stats.combined_gps == 0.0 {
					(
						"Mining Status: Starting miner and awaiting first solution...".to_string(),
						" ".to_string(),
					)
				} else {
					(
						format!(
							"Mining Status: Mining at height {} at {:.*} GPS",
							stats.mining_stats.block_height, 4, stats.mining_stats.combined_gps
						),
						format!(
							"Cuckoo {} - Network Difficulty {}",
							stats.mining_stats.edge_bits,
							stats.mining_stats.network_difficulty.to_string()
						),
					)
				}
			} else {
				(" ".to_string(), " ".to_string())
			}
		};*/
		c.call_on_id("basic_current_status", |t: &mut TextView| {
			t.set_content(basic_status);
		});
		c.call_on_id("connected_peers", |t: &mut TextView| {
			t.set_content(stats.peer_count.to_string());
		});
		c.call_on_id("tip_hash", |t: &mut TextView| {
			t.set_content(stats.head.last_block_h.to_string() + "...");
		});
		c.call_on_id("chain_height", |t: &mut TextView| {
			t.set_content(stats.head.height.to_string());
		});
		c.call_on_id("basic_total_difficulty", |t: &mut TextView| {
			t.set_content(stats.head.total_difficulty.to_string());
		});
		c.call_on_id("basic_header_tip_hash", |t: &mut TextView| {
			t.set_content(stats.header_head.last_block_h.to_string() + "...");
		});
		c.call_on_id("basic_header_chain_height", |t: &mut TextView| {
			t.set_content(stats.header_head.height.to_string());
		});
		c.call_on_id("basic_header_total_difficulty", |t: &mut TextView| {
			t.set_content(stats.header_head.total_difficulty.to_string());
		});
		/*c.call_on_id("basic_mining_config_status", |t: &mut TextView| {
			t.set_content(basic_mining_config_status);
		});
		c.call_on_id("basic_mining_status", |t: &mut TextView| {
			t.set_content(basic_mining_status);
		});
		c.call_on_id("basic_network_info", |t: &mut TextView| {
			t.set_content(basic_network_info);
		});*/
	}
}
<|endoftext|>