<commit_before>#![allow(dead_code)]
#![allow(unused_variables)]
#[macro_use]
extern crate lazy_static;
extern crate ctrlc;
pub mod html;

use std::cell::RefCell;
use std::rc::Rc;
use std::time::Instant;
use std::collections::BTreeMap;
use std::fs::File;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Mutex, Arc};

pub type SpanPtr = RefCell<Span>;
thread_local!(static THREAD_FRAME: RefCell<ThreadFrame> = RefCell::new(ThreadFrame::new()));

lazy_static!{
    static ref SETTED_NAME: Mutex<Option<String>> = Mutex::new(None);
    static ref INTERRUPTED_TICKS: Arc<AtomicUsize> = Arc::new(AtomicUsize::new(0));

}

pub struct ThreadFrame {
    epoch: Instant,
    root: Rc<SpanPtr>,
    events: Vec<Event>,
    dumped_time: usize,
}

struct Event {
    name: &'static str,
    start_ns: u64,
    span: Rc<SpanPtr>,
}

#[derive(Clone)]
pub struct Span {
    value: u64,
    count: u64,
    children: BTreeMap<&'static str, Rc<SpanPtr>>
}

impl Event {
    fn new(name:&'static str, timestamp: u64, span: Rc<SpanPtr>) -> Event {
        Event {
            name: name,
            start_ns: timestamp,
            span: span
        }
    }
}

impl ThreadFrame {

    fn new() -> ThreadFrame {
        let root = Span::new();
        let epoch = Instant::now();
        ThreadFrame {
            epoch: epoch,
            root: root.clone(),
            events: vec![Event::new("Self", ns_since_epoch(epoch), root)],
            dumped_time: 0,
        }
    }

    fn root(&self) -> &SpanPtr {
        &self.root
    }

    fn time_since_start(&self) -> u64 {
        ns_since_epoch(self.epoch)
    }

    fn start_span(&mut self, name: &'static str) {
        let timestamp = ns_since_epoch(self.epoch);
        let span = Span::sub_span(&self.events[self.events.len()-1].span, name);
        self.events.push(Event::new(name, timestamp, span));
    }

    fn end_span(&mut self) {
        let new_time = INTERRUPTED_TICKS.load(Ordering::SeqCst);
        if self.dumped_time < new_time {
            let name: String = SETTED_NAME.lock().unwrap().clone()
                                .expect("Profiler: received interrupt without setted name.");
            File::create(&name)
                    .and_then(|ref mut  file| dump_html(file, &self) )
                    .expect("could not write profiler data");
            self.dumped_time = new_time;
        };

        let timestamp = ns_since_epoch(self.epoch);
        let event = self.events.pop().expect("ThreadFrame::end_span() called events.pop() without a currently running span!");
        let current = event.span;
        // dump self span
        {
            let mut aspan = current.borrow_mut();
            aspan.value += timestamp - event.start_ns;
            aspan.count += 1;
        }
    }

}

fn ns_since_epoch(epoch: Instant) -> u64 {
    let elapsed = epoch.elapsed();
    elapsed.as_secs() * 1000_000_000 + elapsed.subsec_nanos() as u64
}

impl Span {

    fn new() -> Rc<SpanPtr> {
        Rc::new(RefCell::new(Span {
            value: 0,
            count: 0,
            children: BTreeMap::new()
        }))
    }

    fn sub_span(this: &Rc<SpanPtr>, name: &'static str) -> Rc<SpanPtr> {
        let mut parent = this.borrow_mut();
        let  aspan = parent.children.entry(name)
                                     .or_insert_with(|| Span::new());
        aspan.clone()
    }
}

#[cfg(not(feature="nomock"))]
pub fn start(name: &'static str) {

}

#[cfg(not(feature="nomock"))]
pub fn end() {

}

#[cfg(feature="nomock")]
pub fn start(name: &'static str) {
    THREAD_FRAME.with(|thread| thread.borrow_mut().start_span(name));
}

#[cfg(feature="nomock")]
pub fn end() {
    THREAD_FRAME.with(|thread| thread.borrow_mut().end_span());
}

fn spans() -> Rc<SpanPtr> {
    THREAD_FRAME.with(|library| {
        let library = library.borrow();

        let root = library.root.clone();
        root.borrow_mut().value = ns_since_epoch(library.epoch) - library.events[0].start_ns;
        root
    })
}

#[macro_export]
macro_rules! profiler_span{
    ($name:expr, $val:expr) => {
        {
            let _p = $crate::ProfilerSpan::new($name);
            let r = $val;
            drop(_p);
            r
        }
    }
}

#[macro_export]
macro_rules! profiler_next_span{
    ($name:expr) => {
            $crate::end();
            $crate::start($name);
    }
}

pub struct ProfilerSpan<'a> {
    name: &'static str,
    _phantom: PhantomData<&'a ()>
}

impl<'a> Drop for ProfilerSpan<'a> {
    fn drop(&mut self) {
        end();
    }
}

impl<'scope> ProfilerSpan<'scope> {

    pub fn new(name: &'static str) -> ProfilerSpan<'static> {
        start(name);
        ProfilerSpan {
            name: name,
            _phantom:PhantomData
        }
    }

    pub fn next_span(& mut self, name: &'static str) {
        end();
        self.name = name;
        start(self.name);
    }

    pub fn sub_span<'smaller>(&'smaller mut self, name: &'static str) -> ProfilerSpan<'smaller>
    where 'scope: 'smaller {
        ProfilerSpan::new(name)
    }
}

#[cfg(not(feature="nomock"))]
pub fn init_handler(file: String) {
}

#[cfg(feature="nomock")]
pub fn init_handler(file: String) {
    use std::time::{SystemTime, UNIX_EPOCH};

    *SETTED_NAME.lock().unwrap() = Some(file);

    let r = INTERRUPTED_TICKS.clone();
    ::ctrlc::set_handler(move || {
        let secs = SystemTime::now()
                            .duration_since(UNIX_EPOCH)
                            .unwrap().as_secs() as usize;
        r.store(secs, Ordering::SeqCst);
    }).expect("Error setting Ctrl-C handler");
}

pub use html::dump_html;
<commit_msg>make thread frame public<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]
#[macro_use]
extern crate lazy_static;
extern crate ctrlc;
pub mod html;

use std::cell::RefCell;
use std::rc::Rc;
use std::time::Instant;
use std::collections::BTreeMap;
use std::fs::File;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Mutex, Arc};

pub type SpanPtr = RefCell<Span>;
thread_local!(pub static THREAD_FRAME: RefCell<ThreadFrame> = RefCell::new(ThreadFrame::new()));

lazy_static!{
    static ref SETTED_NAME: Mutex<Option<String>> = Mutex::new(None);
    static ref INTERRUPTED_TICKS: Arc<AtomicUsize> = Arc::new(AtomicUsize::new(0));

}

pub struct ThreadFrame {
    epoch: Instant,
    root: Rc<SpanPtr>,
    events: Vec<Event>,
    dumped_time: usize,
}

struct Event {
    name: &'static str,
    start_ns: u64,
    span: Rc<SpanPtr>,
}

#[derive(Clone)]
pub struct Span {
    value: u64,
    count: u64,
    children: BTreeMap<&'static str, Rc<SpanPtr>>
}

impl Event {
    fn new(name:&'static str, timestamp: u64, span: Rc<SpanPtr>) -> Event {
        Event {
            name: name,
            start_ns: timestamp,
            span: span
        }
    }
}

impl ThreadFrame {

    fn new() -> ThreadFrame {
        let root = Span::new();
        let epoch = Instant::now();
        ThreadFrame {
            epoch: epoch,
            root: root.clone(),
            events: vec![Event::new("Self", ns_since_epoch(epoch), root)],
            dumped_time: 0,
        }
    }

    fn root(&self) -> &SpanPtr {
        &self.root
    }

    fn time_since_start(&self) -> u64 {
        ns_since_epoch(self.epoch)
    }

    fn start_span(&mut self, name: &'static str) {
        let timestamp = ns_since_epoch(self.epoch);
        let span = Span::sub_span(&self.events[self.events.len()-1].span, name);
        self.events.push(Event::new(name, timestamp, span));
    }

    fn end_span(&mut self) {
        let new_time = INTERRUPTED_TICKS.load(Ordering::SeqCst);
        if self.dumped_time < new_time {
            let name: String = SETTED_NAME.lock().unwrap().clone()
                                .expect("Profiler: received interrupt without setted name.");
            File::create(&name)
                    .and_then(|ref mut  file| dump_html(file, &self) )
                    .expect("could not write profiler data");
            self.dumped_time = new_time;
        };

        let timestamp = ns_since_epoch(self.epoch);
        let event = self.events.pop().expect("ThreadFrame::end_span() called events.pop() without a currently running span!");
        let current = event.span;
        // dump self span
        {
            let mut aspan = current.borrow_mut();
            aspan.value += timestamp - event.start_ns;
            aspan.count += 1;
        }
    }

}

fn ns_since_epoch(epoch: Instant) -> u64 {
    let elapsed = epoch.elapsed();
    elapsed.as_secs() * 1000_000_000 + elapsed.subsec_nanos() as u64
}

impl Span {

    fn new() -> Rc<SpanPtr> {
        Rc::new(RefCell::new(Span {
            value: 0,
            count: 0,
            children: BTreeMap::new()
        }))
    }

    fn sub_span(this: &Rc<SpanPtr>, name: &'static str) -> Rc<SpanPtr> {
        let mut parent = this.borrow_mut();
        let  aspan = parent.children.entry(name)
                                     .or_insert_with(|| Span::new());
        aspan.clone()
    }
}

#[cfg(not(feature="nomock"))]
pub fn start(name: &'static str) {

}

#[cfg(not(feature="nomock"))]
pub fn end() {

}

#[cfg(feature="nomock")]
pub fn start(name: &'static str) {
    THREAD_FRAME.with(|thread| thread.borrow_mut().start_span(name));
}

#[cfg(feature="nomock")]
pub fn end() {
    THREAD_FRAME.with(|thread| thread.borrow_mut().end_span());
}

fn spans() -> Rc<SpanPtr> {
    THREAD_FRAME.with(|library| {
        let library = library.borrow();

        let root = library.root.clone();
        root.borrow_mut().value = ns_since_epoch(library.epoch) - library.events[0].start_ns;
        root
    })
}

#[macro_export]
macro_rules! profiler_span{
    ($name:expr, $val:expr) => {
        {
            let _p = $crate::ProfilerSpan::new($name);
            let r = $val;
            drop(_p);
            r
        }
    }
}

#[macro_export]
macro_rules! profiler_next_span{
    ($name:expr) => {
            $crate::end();
            $crate::start($name);
    }
}

pub struct ProfilerSpan<'a> {
    name: &'static str,
    _phantom: PhantomData<&'a ()>
}

impl<'a> Drop for ProfilerSpan<'a> {
    fn drop(&mut self) {
        end();
    }
}

impl<'scope> ProfilerSpan<'scope> {

    pub fn new(name: &'static str) -> ProfilerSpan<'static> {
        start(name);
        ProfilerSpan {
            name: name,
            _phantom:PhantomData
        }
    }

    pub fn next_span(& mut self, name: &'static str) {
        end();
        self.name = name;
        start(self.name);
    }

    pub fn sub_span<'smaller>(&'smaller mut self, name: &'static str) -> ProfilerSpan<'smaller>
    where 'scope: 'smaller {
        ProfilerSpan::new(name)
    }
}

#[cfg(not(feature="nomock"))]
pub fn init_handler(file: String) {
}

#[cfg(feature="nomock")]
pub fn init_handler(file: String) {
    use std::time::{SystemTime, UNIX_EPOCH};

    *SETTED_NAME.lock().unwrap() = Some(file);

    let r = INTERRUPTED_TICKS.clone();
    ::ctrlc::set_handler(move || {
        let secs = SystemTime::now()
                            .duration_since(UNIX_EPOCH)
                            .unwrap().as_secs() as usize;
        r.store(secs, Ordering::SeqCst);
    }).expect("Error setting Ctrl-C handler");
}

pub use html::dump_html;
<|endoftext|>


<commit_before>/*code
/*code*/
if true {
    println!("1");
}*/
<commit_msg>remove unecessary source test file<commit_after><|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Utilities to throw exceptions from Rust bindings.

#[cfg(feature = "js_backtrace")]
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::PrototypeList::proto_id_to_name;
use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::conversions::{
    ConversionResult, FromJSValConvertible, ToJSValConvertible,
};
use crate::dom::bindings::str::USVString;
use crate::dom::domexception::{DOMErrorName, DOMException};
use crate::dom::globalscope::GlobalScope;
use crate::realms::InRealm;
use crate::script_runtime::JSContext as SafeJSContext;
#[cfg(feature = "js_backtrace")]
use backtrace::Backtrace;
use js::error::{throw_range_error, throw_type_error};
use js::jsapi::ExceptionStackBehavior;
use js::jsapi::JSContext;
use js::jsapi::JS_ClearPendingException;
use js::jsapi::JS_IsExceptionPending;
use js::jsval::UndefinedValue;
use js::rust::wrappers::JS_ErrorFromException;
use js::rust::wrappers::JS_GetPendingException;
use js::rust::wrappers::JS_SetPendingException;
use js::rust::HandleObject;
use js::rust::MutableHandleValue;
use libc::c_uint;
use std::slice::from_raw_parts;

#[cfg(feature = "js_backtrace")]
thread_local! {
    /// An optional stringified JS backtrace and stringified native backtrace from the
    /// the last DOM exception that was reported.
    static LAST_EXCEPTION_BACKTRACE: DomRefCell<Option<(Option<String>, String)>> = DomRefCell::new(None);
}

/// DOM exceptions that can be thrown by a native DOM method.
#[derive(Clone, Debug, MallocSizeOf)]
pub enum Error {
    /// IndexSizeError DOMException
    IndexSize,
    /// NotFoundError DOMException
    NotFound,
    /// HierarchyRequestError DOMException
    HierarchyRequest,
    /// WrongDocumentError DOMException
    WrongDocument,
    /// InvalidCharacterError DOMException
    InvalidCharacter,
    /// NotSupportedError DOMException
    NotSupported,
    /// InUseAttributeError DOMException
    InUseAttribute,
    /// InvalidStateError DOMException
    InvalidState,
    /// SyntaxError DOMException
    Syntax,
    /// NamespaceError DOMException
    Namespace,
    /// InvalidAccessError DOMException
    InvalidAccess,
    /// SecurityError DOMException
    Security,
    /// NetworkError DOMException
    Network,
    /// AbortError DOMException
    Abort,
    /// TimeoutError DOMException
    Timeout,
    /// InvalidNodeTypeError DOMException
    InvalidNodeType,
    /// DataCloneError DOMException
    DataClone,
    /// NoModificationAllowedError DOMException
    NoModificationAllowed,
    /// QuotaExceededError DOMException
    QuotaExceeded,
    /// TypeMismatchError DOMException
    TypeMismatch,
    /// InvalidModificationError DOMException
    InvalidModification,
    /// NotReadableError DOMException
    NotReadable,
    /// OperationError DOMException
    Operation,

    /// TypeError JavaScript Error
    Type(String),
    /// RangeError JavaScript Error
    Range(String),

    /// A JavaScript exception is already pending.
    JSFailed,
}

/// The return type for IDL operations that can throw DOM exceptions.
pub type Fallible<T> = Result<T, Error>;

/// The return type for IDL operations that can throw DOM exceptions and
/// return `()`.
pub type ErrorResult = Fallible<()>;

/// Set a pending exception for the given `result` on `cx`.
pub fn throw_dom_exception(cx: SafeJSContext, global: &GlobalScope, result: Error) {
    #[cfg(feature = "js_backtrace")]
    {
        capture_stack!(in(*cx) let stack);
        let js_stack = stack.and_then(|s| s.as_string(None));
        let rust_stack = Backtrace::new();
        LAST_EXCEPTION_BACKTRACE.with(|backtrace| {
            *backtrace.borrow_mut() = Some((js_stack, format!("{:?}", rust_stack)));
        });
    }

    let code = match result {
        Error::IndexSize => DOMErrorName::IndexSizeError,
        Error::NotFound => DOMErrorName::NotFoundError,
        Error::HierarchyRequest => DOMErrorName::HierarchyRequestError,
        Error::WrongDocument => DOMErrorName::WrongDocumentError,
        Error::InvalidCharacter => DOMErrorName::InvalidCharacterError,
        Error::NotSupported => DOMErrorName::NotSupportedError,
        Error::InUseAttribute => DOMErrorName::InUseAttributeError,
        Error::InvalidState => DOMErrorName::InvalidStateError,
        Error::Syntax => DOMErrorName::SyntaxError,
        Error::Namespace => DOMErrorName::NamespaceError,
        Error::InvalidAccess => DOMErrorName::InvalidAccessError,
        Error::Security => DOMErrorName::SecurityError,
        Error::Network => DOMErrorName::NetworkError,
        Error::Abort => DOMErrorName::AbortError,
        Error::Timeout => DOMErrorName::TimeoutError,
        Error::InvalidNodeType => DOMErrorName::InvalidNodeTypeError,
        Error::DataClone => DOMErrorName::DataCloneError,
        Error::NoModificationAllowed => DOMErrorName::NoModificationAllowedError,
        Error::QuotaExceeded => DOMErrorName::QuotaExceededError,
        Error::TypeMismatch => DOMErrorName::TypeMismatchError,
        Error::InvalidModification => DOMErrorName::InvalidModificationError,
        Error::NotReadable => DOMErrorName::NotReadableError,
        Error::Operation => DOMErrorName::OperationError,
        Error::Type(message) => unsafe {
            assert!(!JS_IsExceptionPending(*cx));
            throw_type_error(*cx, &message);
            return;
        },
        Error::Range(message) => unsafe {
            assert!(!JS_IsExceptionPending(*cx));
            throw_range_error(*cx, &message);
            return;
        },
        Error::JSFailed => unsafe {
            assert!(JS_IsExceptionPending(*cx));
            return;
        },
    };

    unsafe {
        assert!(!JS_IsExceptionPending(*cx));
        let exception = DOMException::new(global, code);
        rooted!(in(*cx) let mut thrown = UndefinedValue());
        exception.to_jsval(*cx, thrown.handle_mut());
        JS_SetPendingException(*cx, thrown.handle(), ExceptionStackBehavior::Capture);
    }
}

/// A struct encapsulating information about a runtime script error.
pub struct ErrorInfo {
    /// The error message.
    pub message: String,
    /// The file name.
    pub filename: String,
    /// The line number.
    pub lineno: c_uint,
    /// The column number.
    pub column: c_uint,
}

impl ErrorInfo {
    unsafe fn from_native_error(cx: *mut JSContext, object: HandleObject) -> Option<ErrorInfo> {
        let report = JS_ErrorFromException(cx, object);
        if report.is_null() {
            return None;
        }

        let filename = {
            let filename = (*report)._base.filename as *const u8;
            if !filename.is_null() {
                let length = (0..).find(|idx| *filename.offset(*idx) == 0).unwrap();
                let filename = from_raw_parts(filename, length as usize);
                String::from_utf8_lossy(filename).into_owned()
            } else {
                "none".to_string()
            }
        };

        let lineno = (*report)._base.lineno;
        let column = (*report)._base.column;

        let message = {
            let message = (*report)._base.message_.data_ as *const u8;
            let length = (0..).find(|idx| *message.offset(*idx) == 0).unwrap();
            let message = from_raw_parts(message, length as usize);
            String::from_utf8_lossy(message).into_owned()
        };

        Some(ErrorInfo {
            filename: filename,
            message: message,
            lineno: lineno,
            column: column,
        })
    }

    fn from_dom_exception(object: HandleObject, cx: *mut JSContext) -> Option<ErrorInfo> {
        let exception = match root_from_object::<DOMException>(object.get(), cx) {
            Ok(exception) => exception,
            Err(_) => return None,
        };

        Some(ErrorInfo {
            filename: "".to_string(),
            message: exception.stringifier().into(),
            lineno: 0,
            column: 0,
        })
    }
}

/// Report a pending exception, thereby clearing it.
///
/// The `dispatch_event` argument is temporary and non-standard; passing false
/// prevents dispatching the `error` event.
pub unsafe fn report_pending_exception(cx: *mut JSContext, dispatch_event: bool, realm: InRealm) {
    if !JS_IsExceptionPending(cx) {
        return;
    }

    rooted!(in(cx) let mut value = UndefinedValue());
    if !JS_GetPendingException(cx, value.handle_mut()) {
        JS_ClearPendingException(cx);
        error!("Uncaught exception: JS_GetPendingException failed");
        return;
    }

    JS_ClearPendingException(cx);
    let error_info = if value.is_object() {
        rooted!(in(cx) let object = value.to_object());
        ErrorInfo::from_native_error(cx, object.handle())
            .or_else(|| ErrorInfo::from_dom_exception(object.handle(), cx))
            .unwrap_or_else(|| ErrorInfo {
                message: format!("uncaught exception: unknown (can't convert to string)"),
                filename: String::new(),
                lineno: 0,
                column: 0,
            })
    } else {
        match USVString::from_jsval(cx, value.handle(), ()) {
            Ok(ConversionResult::Success(USVString(string))) => ErrorInfo {
                message: format!("uncaught exception: {}", string),
                filename: String::new(),
                lineno: 0,
                column: 0,
            },
            _ => {
                panic!("Uncaught exception: failed to stringify primitive");
            },
        }
    };

    error!(
        "Error at {}:{}:{} {}",
        error_info.filename, error_info.lineno, error_info.column, error_info.message
    );
    #[cfg(feature = "js_backtrace")]
    {
        LAST_EXCEPTION_BACKTRACE.with(|backtrace| {
            if let Some((js_backtrace, rust_backtrace)) = backtrace.borrow_mut().take() {
                if let Some(stack) = js_backtrace {
                    eprintln!("JS backtrace:\n{}", stack);
                }
                eprintln!("Rust backtrace:\n{}", rust_backtrace);
            }
        });
    }

    if dispatch_event {
        GlobalScope::from_context(cx, realm).report_an_error(error_info, value.handle());
    }
}

/// Throw an exception to signal that a `JSObject` can not be converted to a
/// given DOM type.
pub unsafe fn throw_invalid_this(cx: *mut JSContext, proto_id: u16) {
    debug_assert!(!JS_IsExceptionPending(cx));
    let error = format!(
        "\"this\" object does not implement interface {}.",
        proto_id_to_name(proto_id)
    );
    throw_type_error(cx, &error);
}

impl Error {
    /// Convert this error value to a JS value, consuming it in the process.
    pub unsafe fn to_jsval(
        self,
        cx: *mut JSContext,
        global: &GlobalScope,
        rval: MutableHandleValue,
    ) {
        match self {
            Error::JSFailed => (),
            _ => assert!(!JS_IsExceptionPending(cx)),
        }
        throw_dom_exception(SafeJSContext::from_ptr(cx), global, self);
        assert!(JS_IsExceptionPending(cx));
        assert!(JS_GetPendingException(cx, rval));
        JS_ClearPendingException(cx);
    }
}
<commit_msg>Fix building with --feature js_backtrace<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Utilities to throw exceptions from Rust bindings.

#[cfg(feature = "js_backtrace")]
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::PrototypeList::proto_id_to_name;
use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::conversions::{
    ConversionResult, FromJSValConvertible, ToJSValConvertible,
};
use crate::dom::bindings::str::USVString;
use crate::dom::domexception::{DOMErrorName, DOMException};
use crate::dom::globalscope::GlobalScope;
use crate::realms::InRealm;
use crate::script_runtime::JSContext as SafeJSContext;
#[cfg(feature = "js_backtrace")]
use backtrace::Backtrace;
use js::error::{throw_range_error, throw_type_error};
use js::jsapi::ExceptionStackBehavior;
use js::jsapi::JSContext;
use js::jsapi::JS_ClearPendingException;
use js::jsapi::JS_IsExceptionPending;
#[cfg(feature = "js_backtrace")]
use js::jsapi::StackFormat as JSStackFormat;
use js::jsval::UndefinedValue;
use js::rust::wrappers::JS_ErrorFromException;
use js::rust::wrappers::JS_GetPendingException;
use js::rust::wrappers::JS_SetPendingException;
use js::rust::HandleObject;
use js::rust::MutableHandleValue;
use libc::c_uint;
use std::slice::from_raw_parts;

#[cfg(feature = "js_backtrace")]
thread_local! {
    /// An optional stringified JS backtrace and stringified native backtrace from the
    /// the last DOM exception that was reported.
    static LAST_EXCEPTION_BACKTRACE: DomRefCell<Option<(Option<String>, String)>> = DomRefCell::new(None);
}

/// DOM exceptions that can be thrown by a native DOM method.
#[derive(Clone, Debug, MallocSizeOf)]
pub enum Error {
    /// IndexSizeError DOMException
    IndexSize,
    /// NotFoundError DOMException
    NotFound,
    /// HierarchyRequestError DOMException
    HierarchyRequest,
    /// WrongDocumentError DOMException
    WrongDocument,
    /// InvalidCharacterError DOMException
    InvalidCharacter,
    /// NotSupportedError DOMException
    NotSupported,
    /// InUseAttributeError DOMException
    InUseAttribute,
    /// InvalidStateError DOMException
    InvalidState,
    /// SyntaxError DOMException
    Syntax,
    /// NamespaceError DOMException
    Namespace,
    /// InvalidAccessError DOMException
    InvalidAccess,
    /// SecurityError DOMException
    Security,
    /// NetworkError DOMException
    Network,
    /// AbortError DOMException
    Abort,
    /// TimeoutError DOMException
    Timeout,
    /// InvalidNodeTypeError DOMException
    InvalidNodeType,
    /// DataCloneError DOMException
    DataClone,
    /// NoModificationAllowedError DOMException
    NoModificationAllowed,
    /// QuotaExceededError DOMException
    QuotaExceeded,
    /// TypeMismatchError DOMException
    TypeMismatch,
    /// InvalidModificationError DOMException
    InvalidModification,
    /// NotReadableError DOMException
    NotReadable,
    /// OperationError DOMException
    Operation,

    /// TypeError JavaScript Error
    Type(String),
    /// RangeError JavaScript Error
    Range(String),

    /// A JavaScript exception is already pending.
    JSFailed,
}

/// The return type for IDL operations that can throw DOM exceptions.
pub type Fallible<T> = Result<T, Error>;

/// The return type for IDL operations that can throw DOM exceptions and
/// return `()`.
pub type ErrorResult = Fallible<()>;

/// Set a pending exception for the given `result` on `cx`.
pub fn throw_dom_exception(cx: SafeJSContext, global: &GlobalScope, result: Error) {
    #[cfg(feature = "js_backtrace")]
    unsafe {
        capture_stack!(in(*cx) let stack);
        let js_stack = stack.and_then(|s| s.as_string(None, JSStackFormat::Default));
        let rust_stack = Backtrace::new();
        LAST_EXCEPTION_BACKTRACE.with(|backtrace| {
            *backtrace.borrow_mut() = Some((js_stack, format!("{:?}", rust_stack)));
        });
    }

    let code = match result {
        Error::IndexSize => DOMErrorName::IndexSizeError,
        Error::NotFound => DOMErrorName::NotFoundError,
        Error::HierarchyRequest => DOMErrorName::HierarchyRequestError,
        Error::WrongDocument => DOMErrorName::WrongDocumentError,
        Error::InvalidCharacter => DOMErrorName::InvalidCharacterError,
        Error::NotSupported => DOMErrorName::NotSupportedError,
        Error::InUseAttribute => DOMErrorName::InUseAttributeError,
        Error::InvalidState => DOMErrorName::InvalidStateError,
        Error::Syntax => DOMErrorName::SyntaxError,
        Error::Namespace => DOMErrorName::NamespaceError,
        Error::InvalidAccess => DOMErrorName::InvalidAccessError,
        Error::Security => DOMErrorName::SecurityError,
        Error::Network => DOMErrorName::NetworkError,
        Error::Abort => DOMErrorName::AbortError,
        Error::Timeout => DOMErrorName::TimeoutError,
        Error::InvalidNodeType => DOMErrorName::InvalidNodeTypeError,
        Error::DataClone => DOMErrorName::DataCloneError,
        Error::NoModificationAllowed => DOMErrorName::NoModificationAllowedError,
        Error::QuotaExceeded => DOMErrorName::QuotaExceededError,
        Error::TypeMismatch => DOMErrorName::TypeMismatchError,
        Error::InvalidModification => DOMErrorName::InvalidModificationError,
        Error::NotReadable => DOMErrorName::NotReadableError,
        Error::Operation => DOMErrorName::OperationError,
        Error::Type(message) => unsafe {
            assert!(!JS_IsExceptionPending(*cx));
            throw_type_error(*cx, &message);
            return;
        },
        Error::Range(message) => unsafe {
            assert!(!JS_IsExceptionPending(*cx));
            throw_range_error(*cx, &message);
            return;
        },
        Error::JSFailed => unsafe {
            assert!(JS_IsExceptionPending(*cx));
            return;
        },
    };

    unsafe {
        assert!(!JS_IsExceptionPending(*cx));
        let exception = DOMException::new(global, code);
        rooted!(in(*cx) let mut thrown = UndefinedValue());
        exception.to_jsval(*cx, thrown.handle_mut());
        JS_SetPendingException(*cx, thrown.handle(), ExceptionStackBehavior::Capture);
    }
}

/// A struct encapsulating information about a runtime script error.
pub struct ErrorInfo {
    /// The error message.
    pub message: String,
    /// The file name.
    pub filename: String,
    /// The line number.
    pub lineno: c_uint,
    /// The column number.
    pub column: c_uint,
}

impl ErrorInfo {
    unsafe fn from_native_error(cx: *mut JSContext, object: HandleObject) -> Option<ErrorInfo> {
        let report = JS_ErrorFromException(cx, object);
        if report.is_null() {
            return None;
        }

        let filename = {
            let filename = (*report)._base.filename as *const u8;
            if !filename.is_null() {
                let length = (0..).find(|idx| *filename.offset(*idx) == 0).unwrap();
                let filename = from_raw_parts(filename, length as usize);
                String::from_utf8_lossy(filename).into_owned()
            } else {
                "none".to_string()
            }
        };

        let lineno = (*report)._base.lineno;
        let column = (*report)._base.column;

        let message = {
            let message = (*report)._base.message_.data_ as *const u8;
            let length = (0..).find(|idx| *message.offset(*idx) == 0).unwrap();
            let message = from_raw_parts(message, length as usize);
            String::from_utf8_lossy(message).into_owned()
        };

        Some(ErrorInfo {
            filename: filename,
            message: message,
            lineno: lineno,
            column: column,
        })
    }

    fn from_dom_exception(object: HandleObject, cx: *mut JSContext) -> Option<ErrorInfo> {
        let exception = match root_from_object::<DOMException>(object.get(), cx) {
            Ok(exception) => exception,
            Err(_) => return None,
        };

        Some(ErrorInfo {
            filename: "".to_string(),
            message: exception.stringifier().into(),
            lineno: 0,
            column: 0,
        })
    }
}

/// Report a pending exception, thereby clearing it.
///
/// The `dispatch_event` argument is temporary and non-standard; passing false
/// prevents dispatching the `error` event.
pub unsafe fn report_pending_exception(cx: *mut JSContext, dispatch_event: bool, realm: InRealm) {
    if !JS_IsExceptionPending(cx) {
        return;
    }

    rooted!(in(cx) let mut value = UndefinedValue());
    if !JS_GetPendingException(cx, value.handle_mut()) {
        JS_ClearPendingException(cx);
        error!("Uncaught exception: JS_GetPendingException failed");
        return;
    }

    JS_ClearPendingException(cx);
    let error_info = if value.is_object() {
        rooted!(in(cx) let object = value.to_object());
        ErrorInfo::from_native_error(cx, object.handle())
            .or_else(|| ErrorInfo::from_dom_exception(object.handle(), cx))
            .unwrap_or_else(|| ErrorInfo {
                message: format!("uncaught exception: unknown (can't convert to string)"),
                filename: String::new(),
                lineno: 0,
                column: 0,
            })
    } else {
        match USVString::from_jsval(cx, value.handle(), ()) {
            Ok(ConversionResult::Success(USVString(string))) => ErrorInfo {
                message: format!("uncaught exception: {}", string),
                filename: String::new(),
                lineno: 0,
                column: 0,
            },
            _ => {
                panic!("Uncaught exception: failed to stringify primitive");
            },
        }
    };

    error!(
        "Error at {}:{}:{} {}",
        error_info.filename, error_info.lineno, error_info.column, error_info.message
    );
    #[cfg(feature = "js_backtrace")]
    {
        LAST_EXCEPTION_BACKTRACE.with(|backtrace| {
            if let Some((js_backtrace, rust_backtrace)) = backtrace.borrow_mut().take() {
                if let Some(stack) = js_backtrace {
                    eprintln!("JS backtrace:\n{}", stack);
                }
                eprintln!("Rust backtrace:\n{}", rust_backtrace);
            }
        });
    }

    if dispatch_event {
        GlobalScope::from_context(cx, realm).report_an_error(error_info, value.handle());
    }
}

/// Throw an exception to signal that a `JSObject` can not be converted to a
/// given DOM type.
pub unsafe fn throw_invalid_this(cx: *mut JSContext, proto_id: u16) {
    debug_assert!(!JS_IsExceptionPending(cx));
    let error = format!(
        "\"this\" object does not implement interface {}.",
        proto_id_to_name(proto_id)
    );
    throw_type_error(cx, &error);
}

impl Error {
    /// Convert this error value to a JS value, consuming it in the process.
    pub unsafe fn to_jsval(
        self,
        cx: *mut JSContext,
        global: &GlobalScope,
        rval: MutableHandleValue,
    ) {
        match self {
            Error::JSFailed => (),
            _ => assert!(!JS_IsExceptionPending(cx)),
        }
        throw_dom_exception(SafeJSContext::from_ptr(cx), global, self);
        assert!(JS_IsExceptionPending(cx));
        assert!(JS_GetPendingException(cx, rval));
        JS_ClearPendingException(cx);
    }
}
<|endoftext|>


<commit_before>use super::{UIState, WatchState};
use crate::err::{self, Result};
use crate::util;
use chrono::{Duration, Utc};
use smallvec::SmallVec;
use snafu::ResultExt;
use std::collections::VecDeque;
use std::fmt;
use std::io;
use std::sync::mpsc;
use std::thread;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal};
use tui::backend::{self, Backend};
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Color, Modifier, Style};
use tui::terminal::{Frame, Terminal};
use tui::widgets::{Block, Borders, Paragraph, SelectableList, Text, Widget};

macro_rules! create_stat_list {
    ($($header:expr => $value:expr),+) => {
        [$(
            create_stat_list!(h $header),
            create_stat_list!(v $header.len(), $value),
        )+]
    };

    (h $header:expr) => {
        Text::styled(format!("{}\n", $header), Style::default().modifier(Modifier::BOLD))
    };

    (v $len:expr, $value:expr) => {
        Text::styled(format!("{:^width$}\n\n", $value, width = $len), Style::default().modifier(Modifier::ITALIC))
    };
}

pub struct UI<'a, B>
where
    B: Backend,
{
    terminal: Terminal<B>,
    status_log: StatusLog<'a>,
}

impl<'a, B> UI<'a, B>
where
    B: Backend,
{
    pub fn clear(&mut self) -> Result<()> {
        self.terminal.clear().context(err::IO)
    }

    pub fn push_log_status<S>(&mut self, text: S)
    where
        S: Into<String>,
    {
        self.status_log.push(text);
    }

    pub fn log_capture<S, F>(&mut self, text: S, f: F)
    where
        S: Into<String>,
        F: FnOnce() -> Result<()>,
    {
        self.status_log.capture(text, f);
    }

    pub fn draw(&mut self, state: &UIState) -> Result<()> {
        let status_log = &mut self.status_log;

        self.terminal
            .draw(|mut frame| {
                // Top panels for series information
                let horiz_splitter = Layout::default()
                    .direction(Direction::Horizontal)
                    .constraints(
                        [
                            Constraint::Min(20),
                            Constraint::Min(8),
                            Constraint::Percentage(60),
                        ]
                        .as_ref(),
                    )
                    .split(frame.size());

                UI::draw_top_panels(state, &horiz_splitter, &mut frame);

                // Series info panel vertical splitter
                let info_panel_splitter = Layout::default()
                    .direction(Direction::Vertical)
                    .constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_ref())
                    .split(horiz_splitter[2]);

                UI::draw_info_panel(state, &info_panel_splitter, &mut frame);
                UI::draw_status_bar(status_log, info_panel_splitter[1], &mut frame);
            })
            .context(err::IO)
    }

    fn draw_top_panels(state: &UIState, layout: &[Rect], frame: &mut Frame<B>) {
        SelectableList::default()
            .block(Block::default().title("Series").borders(Borders::ALL))
            .items(state.series_names.as_ref())
            .select(Some(state.selected_series))
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().fg(Color::Green).modifier(Modifier::ITALIC))
            .render(frame, layout[0]);

        let season_nums = (1..=state.num_seasons)
            .map(|i| i.to_string())
            .collect::<SmallVec<[_; 4]>>();

        SelectableList::default()
            .block(Block::default().title("Season").borders(Borders::ALL))
            .items(season_nums.as_ref())
            .select(Some(state.season.season_num))
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().fg(Color::Green).modifier(Modifier::ITALIC))
            .highlight_symbol(">")
            .render(frame, layout[1]);
    }

    fn draw_info_panel(state: &UIState, layout: &[Rect], frame: &mut Frame<B>) {
        Block::default()
            .title("Info")
            .borders(Borders::ALL)
            .render(frame, layout[0]);

        let info_layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(layout[0]);

        let season = &state.season;
        let info = &season.series.info;
        let entry = &season.tracker.state;

        // Series title
        {
            let text_items = {
                let mut items = SmallVec::<[_; 2]>::new();

                items.push(Text::styled(
                    &info.title,
                    Style::default().modifier(Modifier::BOLD),
                ));

                if entry.needs_sync() {
                    items.push(Text::styled(
                        " [*]",
                        Style::default().modifier(Modifier::ITALIC),
                    ));
                }

                items
            };

            Paragraph::new(text_items.iter())
                .alignment(Alignment::Center)
                .render(frame, info_layout[0]);
        }

        // Items in panel
        let stat_layout = Layout::default()
            .direction(Direction::Horizontal)
            .constraints(
                [
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                ]
                .as_ref(),
            )
            .split(info_layout[1]);

        {
            let left_items = create_stat_list!(
                "Watch Time" => season.value_cache.watch_time,
                "Time Left" => season.value_cache.watch_time_left,
                "Episode Length" => season.value_cache.episode_length
            );

            Paragraph::new(left_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[0]);
        }

        {
            let center_items = create_stat_list!(
                "Progress" => season.value_cache.progress,
                "Score" => season.value_cache.score,
                "Status" => entry.status()
            );

            Paragraph::new(center_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[1]);
        }

        {
            let right_items = create_stat_list!(
                "Start Date" => season.value_cache.start_date,
                "Finish Date" => season.value_cache.end_date,
                "Rewatched" => entry.times_rewatched()
            );

            Paragraph::new(right_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[2]);
        }

        // Watch time needed indicator at bottom
        match season.watch_state {
            WatchState::Idle => (),
            WatchState::Watching(_, progress_time, _) => {
                let watch_time = progress_time - Utc::now();
                let watch_secs = watch_time.num_seconds();

                if watch_secs > 0 {
                    let remaining_mins = watch_secs as f32 / 60.0;

                    let text_str = format!(
                        "{} Remaining Until Progression",
                        util::ms_from_mins(remaining_mins)
                    );

                    let text = Text::styled(text_str, Style::default().modifier(Modifier::BOLD));

                    Paragraph::new([text].iter())
                        .alignment(Alignment::Center)
                        .render(frame, info_layout[2]);
                }
            }
        }
    }

    fn draw_status_bar(log: &mut StatusLog, layout: Rect, frame: &mut Frame<B>) {
        log.prepare_to_draw(layout);

        Paragraph::new(log.draw_items().iter())
            .block(Block::default().title("Status").borders(Borders::ALL))
            .render(frame, layout);
    }
}

pub type TermionBackend = backend::TermionBackend<RawTerminal<io::Stdout>>;

impl<'a> UI<'a, TermionBackend> {
    pub fn init() -> Result<UI<'a, TermionBackend>> {
        let stdout = io::stdout().into_raw_mode().context(err::IO)?;
        let backend = TermionBackend::new(stdout);
        let mut terminal = Terminal::new(backend).context(err::IO)?;

        terminal.clear().context(err::IO)?;
        terminal.hide_cursor().context(err::IO)?;

        Ok(UI {
            terminal,
            status_log: StatusLog::new(),
        })
    }
}

pub enum Event {
    Input(Key),
    Tick,
}

pub struct Events(mpsc::Receiver<Event>);

impl Events {
    pub fn new(tick_rate: Duration) -> Events {
        let (tx, rx) = mpsc::channel();

        Events::spawn_key_handler(tx.clone());
        Events::spawn_tick_handler(tick_rate, tx);

        Events(rx)
    }

    fn spawn_key_handler(tx: mpsc::Sender<Event>) -> thread::JoinHandle<()> {
        let stdin = io::stdin();

        thread::spawn(move || {
            for event in stdin.keys() {
                if let Ok(key) = event {
                    tx.send(Event::Input(key)).unwrap();
                }
            }
        })
    }

    fn spawn_tick_handler(tick_rate: Duration, tx: mpsc::Sender<Event>) -> thread::JoinHandle<()> {
        let tick_rate = tick_rate
            .to_std()
            .unwrap_or_else(|_| std::time::Duration::from_secs(1));

        thread::spawn(move || loop {
            thread::sleep(tick_rate);
            tx.send(Event::Tick).unwrap();
        })
    }

    pub fn next(&self) -> Result<Event> {
        self.0.recv().context(err::MPSCRecv)
    }
}

pub enum LogItemStatus {
    Ok,
    Pending,
    Failed,
}

impl LogItemStatus {
    pub fn is_resolved(&self) -> bool {
        match self {
            LogItemStatus::Ok | LogItemStatus::Failed => true,
            LogItemStatus::Pending => false,
        }
    }
}

impl fmt::Display for LogItemStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            LogItemStatus::Ok => write!(f, "ok"),
            LogItemStatus::Pending => write!(f, "pending"),
            LogItemStatus::Failed => write!(f, "failed"),
        }
    }
}

pub struct LogItem {
    pub text: String,
    pub status: LogItemStatus,
}

impl LogItem {
    pub fn new<S>(text: S) -> LogItem
    where
        S: Into<String>,
    {
        LogItem {
            text: text.into(),
            status: LogItemStatus::Pending,
        }
    }
}

impl<T> From<T> for LogItem
where
    T: Into<String>,
{
    fn from(value: T) -> Self {
        LogItem::new(value)
    }
}

pub struct StatusLog<'a> {
    items: VecDeque<LogItem>,
    formatted: Vec<Text<'a>>,
    max_items: u16,
}

impl<'a> StatusLog<'a> {
    pub fn new() -> StatusLog<'a> {
        StatusLog {
            items: VecDeque::new(),
            formatted: Vec::new(),
            max_items: 0,
        }
    }

    fn rebuild(&mut self) {
        self.formatted.clear();

        for item in &self.items {
            let value_text = if item.status.is_resolved() {
                format!("{}... ", item.text)
            } else {
                format!("{}...\n", item.text)
            };

            self.formatted.push(Text::raw(value_text));

            if !item.status.is_resolved() {
                continue;
            }

            let status_color = match item.status {
                LogItemStatus::Ok => Color::Green,
                LogItemStatus::Pending => Color::Yellow,
                LogItemStatus::Failed => Color::Red,
            };

            let status = Text::styled(
                format!("{}\n", item.status),
                Style::default().fg(status_color),
            );

            self.formatted.push(status);
        }
    }

    pub fn push<I>(&mut self, item: I)
    where
        I: Into<LogItem>,
    {
        self.trim();
        self.items.push_back(item.into());
        self.rebuild();
    }

    pub fn set_status_on_last(&mut self, status: LogItemStatus) {
        let last = match self.items.get_mut(self.items.len() - 1) {
            Some(last) => last,
            None => return,
        };

        last.status = status;
        self.rebuild();
    }

    pub fn capture<S, F>(&mut self, text: S, f: F)
    where
        S: Into<String>,
        F: FnOnce() -> Result<()>,
    {
        self.push(text);

        let status = match f() {
            Ok(_) => LogItemStatus::Ok,
            // TODO: display error
            Err(_) => LogItemStatus::Failed,
        };

        self.set_status_on_last(status);
    }

    pub fn prepare_to_draw(&mut self, size: Rect) {
        // We assume a border is around the list, so subtract 2 from the height
        self.max_items = size.height.saturating_sub(2);
        self.trim();
    }

    fn trim(&mut self) {
        while !self.items.is_empty() && self.items.len() >= self.max_items as usize {
            self.items.pop_front();
        }
    }

    /// Returns the log items in a ready-to-draw form.
    ///
    /// Note that this is intended by used with the Paragraph widget type.
    pub fn draw_items(&self) -> &Vec<Text<'a>> {
        &self.formatted
    }
}
<commit_msg>[anup] Swap order of && expression in StatusLog::trim<commit_after>use super::{UIState, WatchState};
use crate::err::{self, Result};
use crate::util;
use chrono::{Duration, Utc};
use smallvec::SmallVec;
use snafu::ResultExt;
use std::collections::VecDeque;
use std::fmt;
use std::io;
use std::sync::mpsc;
use std::thread;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal};
use tui::backend::{self, Backend};
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Color, Modifier, Style};
use tui::terminal::{Frame, Terminal};
use tui::widgets::{Block, Borders, Paragraph, SelectableList, Text, Widget};

macro_rules! create_stat_list {
    ($($header:expr => $value:expr),+) => {
        [$(
            create_stat_list!(h $header),
            create_stat_list!(v $header.len(), $value),
        )+]
    };

    (h $header:expr) => {
        Text::styled(format!("{}\n", $header), Style::default().modifier(Modifier::BOLD))
    };

    (v $len:expr, $value:expr) => {
        Text::styled(format!("{:^width$}\n\n", $value, width = $len), Style::default().modifier(Modifier::ITALIC))
    };
}

pub struct UI<'a, B>
where
    B: Backend,
{
    terminal: Terminal<B>,
    status_log: StatusLog<'a>,
}

impl<'a, B> UI<'a, B>
where
    B: Backend,
{
    pub fn clear(&mut self) -> Result<()> {
        self.terminal.clear().context(err::IO)
    }

    pub fn push_log_status<S>(&mut self, text: S)
    where
        S: Into<String>,
    {
        self.status_log.push(text);
    }

    pub fn log_capture<S, F>(&mut self, text: S, f: F)
    where
        S: Into<String>,
        F: FnOnce() -> Result<()>,
    {
        self.status_log.capture(text, f);
    }

    pub fn draw(&mut self, state: &UIState) -> Result<()> {
        let status_log = &mut self.status_log;

        self.terminal
            .draw(|mut frame| {
                // Top panels for series information
                let horiz_splitter = Layout::default()
                    .direction(Direction::Horizontal)
                    .constraints(
                        [
                            Constraint::Min(20),
                            Constraint::Min(8),
                            Constraint::Percentage(60),
                        ]
                        .as_ref(),
                    )
                    .split(frame.size());

                UI::draw_top_panels(state, &horiz_splitter, &mut frame);

                // Series info panel vertical splitter
                let info_panel_splitter = Layout::default()
                    .direction(Direction::Vertical)
                    .constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_ref())
                    .split(horiz_splitter[2]);

                UI::draw_info_panel(state, &info_panel_splitter, &mut frame);
                UI::draw_status_bar(status_log, info_panel_splitter[1], &mut frame);
            })
            .context(err::IO)
    }

    fn draw_top_panels(state: &UIState, layout: &[Rect], frame: &mut Frame<B>) {
        SelectableList::default()
            .block(Block::default().title("Series").borders(Borders::ALL))
            .items(state.series_names.as_ref())
            .select(Some(state.selected_series))
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().fg(Color::Green).modifier(Modifier::ITALIC))
            .render(frame, layout[0]);

        let season_nums = (1..=state.num_seasons)
            .map(|i| i.to_string())
            .collect::<SmallVec<[_; 4]>>();

        SelectableList::default()
            .block(Block::default().title("Season").borders(Borders::ALL))
            .items(season_nums.as_ref())
            .select(Some(state.season.season_num))
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().fg(Color::Green).modifier(Modifier::ITALIC))
            .highlight_symbol(">")
            .render(frame, layout[1]);
    }

    fn draw_info_panel(state: &UIState, layout: &[Rect], frame: &mut Frame<B>) {
        Block::default()
            .title("Info")
            .borders(Borders::ALL)
            .render(frame, layout[0]);

        let info_layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(layout[0]);

        let season = &state.season;
        let info = &season.series.info;
        let entry = &season.tracker.state;

        // Series title
        {
            let text_items = {
                let mut items = SmallVec::<[_; 2]>::new();

                items.push(Text::styled(
                    &info.title,
                    Style::default().modifier(Modifier::BOLD),
                ));

                if entry.needs_sync() {
                    items.push(Text::styled(
                        " [*]",
                        Style::default().modifier(Modifier::ITALIC),
                    ));
                }

                items
            };

            Paragraph::new(text_items.iter())
                .alignment(Alignment::Center)
                .render(frame, info_layout[0]);
        }

        // Items in panel
        let stat_layout = Layout::default()
            .direction(Direction::Horizontal)
            .constraints(
                [
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                ]
                .as_ref(),
            )
            .split(info_layout[1]);

        {
            let left_items = create_stat_list!(
                "Watch Time" => season.value_cache.watch_time,
                "Time Left" => season.value_cache.watch_time_left,
                "Episode Length" => season.value_cache.episode_length
            );

            Paragraph::new(left_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[0]);
        }

        {
            let center_items = create_stat_list!(
                "Progress" => season.value_cache.progress,
                "Score" => season.value_cache.score,
                "Status" => entry.status()
            );

            Paragraph::new(center_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[1]);
        }

        {
            let right_items = create_stat_list!(
                "Start Date" => season.value_cache.start_date,
                "Finish Date" => season.value_cache.end_date,
                "Rewatched" => entry.times_rewatched()
            );

            Paragraph::new(right_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[2]);
        }

        // Watch time needed indicator at bottom
        match season.watch_state {
            WatchState::Idle => (),
            WatchState::Watching(_, progress_time, _) => {
                let watch_time = progress_time - Utc::now();
                let watch_secs = watch_time.num_seconds();

                if watch_secs > 0 {
                    let remaining_mins = watch_secs as f32 / 60.0;

                    let text_str = format!(
                        "{} Remaining Until Progression",
                        util::ms_from_mins(remaining_mins)
                    );

                    let text = Text::styled(text_str, Style::default().modifier(Modifier::BOLD));

                    Paragraph::new([text].iter())
                        .alignment(Alignment::Center)
                        .render(frame, info_layout[2]);
                }
            }
        }
    }

    fn draw_status_bar(log: &mut StatusLog, layout: Rect, frame: &mut Frame<B>) {
        log.prepare_to_draw(layout);

        Paragraph::new(log.draw_items().iter())
            .block(Block::default().title("Status").borders(Borders::ALL))
            .render(frame, layout);
    }
}

pub type TermionBackend = backend::TermionBackend<RawTerminal<io::Stdout>>;

impl<'a> UI<'a, TermionBackend> {
    pub fn init() -> Result<UI<'a, TermionBackend>> {
        let stdout = io::stdout().into_raw_mode().context(err::IO)?;
        let backend = TermionBackend::new(stdout);
        let mut terminal = Terminal::new(backend).context(err::IO)?;

        terminal.clear().context(err::IO)?;
        terminal.hide_cursor().context(err::IO)?;

        Ok(UI {
            terminal,
            status_log: StatusLog::new(),
        })
    }
}

pub enum Event {
    Input(Key),
    Tick,
}

pub struct Events(mpsc::Receiver<Event>);

impl Events {
    pub fn new(tick_rate: Duration) -> Events {
        let (tx, rx) = mpsc::channel();

        Events::spawn_key_handler(tx.clone());
        Events::spawn_tick_handler(tick_rate, tx);

        Events(rx)
    }

    fn spawn_key_handler(tx: mpsc::Sender<Event>) -> thread::JoinHandle<()> {
        let stdin = io::stdin();

        thread::spawn(move || {
            for event in stdin.keys() {
                if let Ok(key) = event {
                    tx.send(Event::Input(key)).unwrap();
                }
            }
        })
    }

    fn spawn_tick_handler(tick_rate: Duration, tx: mpsc::Sender<Event>) -> thread::JoinHandle<()> {
        let tick_rate = tick_rate
            .to_std()
            .unwrap_or_else(|_| std::time::Duration::from_secs(1));

        thread::spawn(move || loop {
            thread::sleep(tick_rate);
            tx.send(Event::Tick).unwrap();
        })
    }

    pub fn next(&self) -> Result<Event> {
        self.0.recv().context(err::MPSCRecv)
    }
}

pub enum LogItemStatus {
    Ok,
    Pending,
    Failed,
}

impl LogItemStatus {
    pub fn is_resolved(&self) -> bool {
        match self {
            LogItemStatus::Ok | LogItemStatus::Failed => true,
            LogItemStatus::Pending => false,
        }
    }
}

impl fmt::Display for LogItemStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            LogItemStatus::Ok => write!(f, "ok"),
            LogItemStatus::Pending => write!(f, "pending"),
            LogItemStatus::Failed => write!(f, "failed"),
        }
    }
}

pub struct LogItem {
    pub text: String,
    pub status: LogItemStatus,
}

impl LogItem {
    pub fn new<S>(text: S) -> LogItem
    where
        S: Into<String>,
    {
        LogItem {
            text: text.into(),
            status: LogItemStatus::Pending,
        }
    }
}

impl<T> From<T> for LogItem
where
    T: Into<String>,
{
    fn from(value: T) -> Self {
        LogItem::new(value)
    }
}

pub struct StatusLog<'a> {
    items: VecDeque<LogItem>,
    formatted: Vec<Text<'a>>,
    max_items: u16,
}

impl<'a> StatusLog<'a> {
    pub fn new() -> StatusLog<'a> {
        StatusLog {
            items: VecDeque::new(),
            formatted: Vec::new(),
            max_items: 0,
        }
    }

    fn rebuild(&mut self) {
        self.formatted.clear();

        for item in &self.items {
            let value_text = if item.status.is_resolved() {
                format!("{}... ", item.text)
            } else {
                format!("{}...\n", item.text)
            };

            self.formatted.push(Text::raw(value_text));

            if !item.status.is_resolved() {
                continue;
            }

            let status_color = match item.status {
                LogItemStatus::Ok => Color::Green,
                LogItemStatus::Pending => Color::Yellow,
                LogItemStatus::Failed => Color::Red,
            };

            let status = Text::styled(
                format!("{}\n", item.status),
                Style::default().fg(status_color),
            );

            self.formatted.push(status);
        }
    }

    pub fn push<I>(&mut self, item: I)
    where
        I: Into<LogItem>,
    {
        self.trim();
        self.items.push_back(item.into());
        self.rebuild();
    }

    pub fn set_status_on_last(&mut self, status: LogItemStatus) {
        let last = match self.items.get_mut(self.items.len() - 1) {
            Some(last) => last,
            None => return,
        };

        last.status = status;
        self.rebuild();
    }

    pub fn capture<S, F>(&mut self, text: S, f: F)
    where
        S: Into<String>,
        F: FnOnce() -> Result<()>,
    {
        self.push(text);

        let status = match f() {
            Ok(_) => LogItemStatus::Ok,
            // TODO: display error
            Err(_) => LogItemStatus::Failed,
        };

        self.set_status_on_last(status);
    }

    pub fn prepare_to_draw(&mut self, size: Rect) {
        // We assume a border is around the list, so subtract 2 from the height
        self.max_items = size.height.saturating_sub(2);
        self.trim();
    }

    fn trim(&mut self) {
        while self.items.len() >= self.max_items as usize && !self.items.is_empty() {
            self.items.pop_front();
        }
    }

    /// Returns the log items in a ready-to-draw form.
    ///
    /// Note that this is intended by used with the Paragraph widget type.
    pub fn draw_items(&self) -> &Vec<Text<'a>> {
        &self.formatted
    }
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

use crate::formatter::enums_shared::SymbolFlags;
use crate::formatter::tests::enums::OptionsProps;
use crate::formatter::tests::opt_value::OptionValue;
use crate::formatter::tests::options_parser::parse_option;
use crate::formatter::tests::sym_res_test_case::*;
use crate::test_utils::from_str_conv::*;
use crate::test_utils::get_default_ip;
use crate::MemorySize;
use alloc::string::String;
use alloc::vec::Vec;
use core::iter::IntoIterator;
use core::u32;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, Lines};
use std::path::Path;

pub(super) struct SymbolResolverTestParser<'a> {
	filename: String,
	lines: Lines<BufReader<File>>,
	ignored: &'a mut HashSet<u32>,
}

impl<'a> SymbolResolverTestParser<'a> {
	pub(super) fn new(filename: &Path, ignored: &'a mut HashSet<u32>) -> Self {
		let display_filename = filename.display().to_string();
		let file = File::open(filename).unwrap_or_else(|_| panic!("Couldn't open file {}", display_filename));
		let lines = BufReader::new(file).lines();
		Self { filename: display_filename, lines, ignored }
	}
}

impl<'a> IntoIterator for SymbolResolverTestParser<'a> {
	type Item = SymbolResolverTestCase;
	type IntoIter = IntoIter<'a>;

	fn into_iter(self) -> Self::IntoIter {
		// GENERATOR-BEGIN: OptionsDict
		// ⚠This was generated by GENERATOR!🦹♂
		let mut to_flags: HashMap<&'static str, u32> = HashMap::with_capacity(2);
		let _ = to_flags.insert("rel", SymbolFlags::RELATIVE);
		let _ = to_flags.insert("signed", SymbolFlags::SIGNED);
		// GENERATOR-END: OptionsDict

		IntoIter { filename: self.filename, lines: self.lines, ignored: self.ignored, test_case_number: 0, line_number: 0, to_flags }
	}
}

pub(super) struct IntoIter<'a> {
	filename: String,
	lines: Lines<BufReader<File>>,
	ignored: &'a mut HashSet<u32>,
	test_case_number: u32,
	line_number: u32,
	to_flags: HashMap<&'static str, u32>,
}

impl Iterator for IntoIter<'_> {
	type Item = SymbolResolverTestCase;

	fn next(&mut self) -> Option<Self::Item> {
		loop {
			let result = match self.lines.next()? {
				Ok(line) => {
					self.line_number += 1;
					if line.is_empty() || line.starts_with('#') {
						continue;
					}
					self.test_case_number += 1;
					self.read_next_test_case(line, self.line_number)
				}
				Err(err) => Err(err.to_string()),
			};
			match result {
				Ok(tc) => {
					if let Some(tc) = tc {
						return Some(tc);
					} else {
						let _ = self.ignored.insert(self.test_case_number - 1);
						continue;
					}
				}
				Err(err) => panic!("Error parsing symbol resolver test case file '{}', line {}: {}", self.filename, self.line_number, err),
			}
		}
	}
}

impl IntoIter<'_> {
	fn read_next_test_case(&self, line: String, line_number: u32) -> Result<Option<SymbolResolverTestCase>, String> {
		let elems: Vec<_> = line.split(',').collect();
		const SYM_RES_INDEX: usize = 4;
		if elems.len() < SYM_RES_INDEX {
			return Err(format!("Invalid number of commas: {}", elems.len() - 1));
		}

		let bitness = to_u32(elems[0])?;
		let ip = get_default_ip(bitness);
		let hex_bytes = String::from(elems[1].trim());
		let _ = to_vec_u8(&hex_bytes)?;
		if is_ignored_code(elems[2]) {
			return Ok(None);
		}
		let code = to_code(elems[2])?;

		let mut options: Vec<(OptionsProps, OptionValue)> = Vec::new();
		for value in elems[3].split_whitespace() {
			if value.is_empty() {
				continue;
			}
			options.push(parse_option(value)?);
		}

		let mut symbol_results: Vec<SymbolResultTestCase> = Vec::with_capacity(elems.len() - SYM_RES_INDEX);
		for elem in &elems[SYM_RES_INDEX..] {
			let sym_parts: Vec<_> = elem.split(';').collect();
			if sym_parts.len() != 5 {
				return Err(format!("Invalid number of semicolons: {}", sym_parts.len() - 1));
			}

			let address = to_u64(sym_parts[0])?;
			let symbol_address = to_u64(sym_parts[1])?;
			let address_size = to_u32(sym_parts[2])?;
			let symbol_parts: Vec<String> = sym_parts[3].split('|').map(String::from).collect();

			let mut memory_size: Option<MemorySize> = None;
			let mut flags = SymbolFlags::NONE;
			for value in sym_parts[4].split_whitespace() {
				if value.is_empty() {
					continue;
				}
				if let Some(f) = self.to_flags.get(value) {
					flags |= *f;
				} else {
					memory_size = Some(to_memory_size(value)?);
				}
			}
			symbol_results.push(SymbolResultTestCase { address, symbol_address, address_size, flags, memory_size, symbol_parts })
		}

		let decoder_options = OptionValue::get_decoder_options(&options);
		Ok(Some(SymbolResolverTestCase { bitness, hex_bytes, ip, decoder_options, code, options, line_number, symbol_results }))
	}
}
<commit_msg>Fix warning<commit_after>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

use crate::formatter::enums_shared::SymbolFlags;
use crate::formatter::tests::enums::OptionsProps;
use crate::formatter::tests::opt_value::OptionValue;
use crate::formatter::tests::options_parser::parse_option;
use crate::formatter::tests::sym_res_test_case::*;
use crate::test_utils::from_str_conv::*;
use crate::test_utils::get_default_ip;
use crate::MemorySize;
use alloc::string::String;
use alloc::vec::Vec;
use core::iter::IntoIterator;
use core::u32;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, Lines};
use std::path::Path;

pub(super) struct SymbolResolverTestParser<'a> {
	filename: String,
	lines: Lines<BufReader<File>>,
	ignored: &'a mut HashSet<u32>,
}

impl<'a> SymbolResolverTestParser<'a> {
	pub(super) fn new(filename: &Path, ignored: &'a mut HashSet<u32>) -> Self {
		let display_filename = filename.display().to_string();
		let file = File::open(filename).unwrap_or_else(|_| panic!("Couldn't open file {}", display_filename));
		let lines = BufReader::new(file).lines();
		Self { filename: display_filename, lines, ignored }
	}
}

impl<'a> IntoIterator for SymbolResolverTestParser<'a> {
	type Item = SymbolResolverTestCase;
	type IntoIter = IntoIter<'a>;

	fn into_iter(self) -> Self::IntoIter {
		// GENERATOR-BEGIN: OptionsDict
		// ⚠This was generated by GENERATOR!🦹♂
		let mut to_flags: HashMap<&'static str, u32> = HashMap::with_capacity(2);
		let _ = to_flags.insert("rel", SymbolFlags::RELATIVE);
		let _ = to_flags.insert("signed", SymbolFlags::SIGNED);
		// GENERATOR-END: OptionsDict

		IntoIter { filename: self.filename, lines: self.lines, ignored: self.ignored, test_case_number: 0, line_number: 0, to_flags }
	}
}

pub(super) struct IntoIter<'a> {
	filename: String,
	lines: Lines<BufReader<File>>,
	ignored: &'a mut HashSet<u32>,
	test_case_number: u32,
	line_number: u32,
	to_flags: HashMap<&'static str, u32>,
}

impl Iterator for IntoIter<'_> {
	type Item = SymbolResolverTestCase;

	fn next(&mut self) -> Option<Self::Item> {
		loop {
			let result = match self.lines.next()? {
				Ok(line) => {
					self.line_number += 1;
					if line.is_empty() || line.starts_with('#') {
						continue;
					}
					self.test_case_number += 1;
					self.read_next_test_case(line, self.line_number)
				}
				Err(err) => Err(err.to_string()),
			};
			match result {
				Ok(tc) => {
					if let Some(tc) = tc {
						return Some(tc);
					} else {
						let _ = self.ignored.insert(self.test_case_number - 1);
						continue;
					}
				}
				Err(err) => panic!("Error parsing symbol resolver test case file '{}', line {}: {}", self.filename, self.line_number, err),
			}
		}
	}
}

impl IntoIter<'_> {
	fn read_next_test_case(&self, line: String, line_number: u32) -> Result<Option<SymbolResolverTestCase>, String> {
		let elems: Vec<_> = line.split(',').collect();
		const SYM_RES_INDEX: usize = 4;
		if elems.len() < SYM_RES_INDEX {
			return Err(format!("Invalid number of commas: {}", elems.len() - 1));
		}

		let bitness = to_u32(elems[0])?;
		let ip = get_default_ip(bitness);
		let hex_bytes = String::from(elems[1].trim());
		let _ = to_vec_u8(&hex_bytes)?;
		if is_ignored_code(elems[2]) {
			return Ok(None);
		}
		let code = to_code(elems[2])?;

		let mut options: Vec<(OptionsProps, OptionValue)> = Vec::new();
		for value in elems[3].split_whitespace() {
			if value.is_empty() {
				continue;
			}
			options.push(parse_option(value)?);
		}

		let mut symbol_results: Vec<SymbolResultTestCase> = Vec::with_capacity(elems.len() - SYM_RES_INDEX);
		for elem in &elems[SYM_RES_INDEX..] {
			let sym_parts: Vec<_> = elem.split(';').collect();
			if sym_parts.len() != 5 {
				return Err(format!("Invalid number of semicolons: {}", sym_parts.len() - 1));
			}

			let address = to_u64(sym_parts[0])?;
			let symbol_address = to_u64(sym_parts[1])?;
			let address_size = to_u32(sym_parts[2])?;
			let symbol_parts: Vec<String> = sym_parts[3].split('|').map(String::from).collect();

			let mut memory_size: Option<MemorySize> = None;
			let mut flags = SymbolFlags::NONE;
			for value in sym_parts[4].split_whitespace() {
				if value.is_empty() {
					continue;
				}
				if let Some(f) = self.to_flags.get(value) {
					flags |= *f;
				} else {
					memory_size = Some(to_memory_size(value)?);
				}
			}
			symbol_results.push(SymbolResultTestCase { address, symbol_address, address_size, flags, memory_size, symbol_parts })
		}

		let decoder_options = OptionValue::get_decoder_options(&options);
		Ok(Some(SymbolResolverTestCase { bitness, hex_bytes, ip, decoder_options, line_number, code, options, symbol_results }))
	}
}
<|endoftext|>


<commit_before>// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[link_args = "-L. -lcairo -lazure -lstdc++"]
#[nolink]
extern mod m { }

#[cfg(target_os = "macos")]
#[link_args = "-L. -lcairo -lazure -lstdc++ -framework ApplicationServices \
			   -lskia -framework IOSurface -lobjc -framework OpenGL \
			   -framework Foundation -framework QuartzCore"]
#[nolink]
extern mod m { }
<commit_msg>Link to skia on linux<commit_after>// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[link_args = "-L. -lcairo -lazure -lstdc++ -lskia"]
#[nolink]
extern mod m { }

#[cfg(target_os = "macos")]
#[link_args = "-L. -lcairo -lazure -lstdc++ -framework ApplicationServices \
			   -lskia -framework IOSurface -lobjc -framework OpenGL \
			   -framework Foundation -framework QuartzCore"]
#[nolink]
extern mod m { }
<|endoftext|>


<commit_before><commit_msg>Removed a leftover<commit_after><|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.

//! All the rules required for a cryptocurrency to have reach consensus across
//! the whole network are complex and hard to completely isolate. Some can be
//! simple parameters (like block reward), others complex algorithms (like
//! Merkle sum trees or reorg rules). However, as long as they're simple
//! enough, consensus-relevant constants and short functions should be kept
//! here.

use std::cmp::max;
use std::fmt;

use core::target::Difficulty;
use global;

/// A grin is divisible to 10^9, following the SI prefixes
pub const GRIN_BASE: u64 = 1_000_000_000;
/// Milligrin, a thousand of a grin
pub const MILLI_GRIN: u64 = GRIN_BASE / 1_000;
/// Microgrin, a thousand of a milligrin
pub const MICRO_GRIN: u64 = MILLI_GRIN / 1_000;
/// Nanogrin, smallest unit, takes a billion to make a grin
pub const NANO_GRIN: u64 = 1;

/// The block subsidy amount, one grin per second on average
pub const REWARD: u64 = 60 * GRIN_BASE;

/// Actual block reward for a given total fee amount
pub fn reward(fee: u64) -> u64 {
	REWARD + fee
}

/// Number of blocks before a coinbase matures and can be spent
pub const COINBASE_MATURITY: u64 = 1_000;

/// Block interval, in seconds, the network will tune its next_target for. Note
/// that we may reduce this value in the future as we get more data on mining
/// with Cuckoo Cycle, networks improve and block propagation is optimized
/// (adjusting the reward accordingly).
pub const BLOCK_TIME_SEC: u64 = 60;

/// Cuckoo-cycle proof size (cycle length)
pub const PROOFSIZE: usize = 42;

/// Default Cuckoo Cycle size shift used for mining and validating.
pub const DEFAULT_SIZESHIFT: u8 = 30;

/// Default Cuckoo Cycle easiness, high enough to have good likeliness to find
/// a solution.
pub const EASINESS: u32 = 50;

/// Default number of blocks in the past when cross-block cut-through will start
/// happening. Needs to be long enough to not overlap with a long reorg.
/// Rational
/// behind the value is the longest bitcoin fork was about 30 blocks, so 5h. We
/// add an order of magnitude to be safe and round to 48h of blocks to make it
/// easier to reason about.
pub const CUT_THROUGH_HORIZON: u32 = 48 * 3600 / (BLOCK_TIME_SEC as u32);

/// The maximum size we're willing to accept for any message. Enforced by the
/// peer-to-peer networking layer only for DoS protection.
pub const MAX_MSG_LEN: u64 = 20_000_000;

/// Weight of an input when counted against the max block weight capacity
pub const BLOCK_INPUT_WEIGHT: usize = 1;

/// Weight of an output when counted against the max block weight capacity
pub const BLOCK_OUTPUT_WEIGHT: usize = 10;

/// Weight of a kernel when counted against the max block weight capacity
pub const BLOCK_KERNEL_WEIGHT: usize = 2;

/// Total maximum block weight
pub const MAX_BLOCK_WEIGHT: usize = 80_000;

/// Maximum inputs for a block (issue#261)
/// Hundreds of inputs + 1 output might be slow to validate (issue#258)
pub const MAX_BLOCK_INPUTS: usize = 300_000; // soft fork down when too_high

/// Maximum inputs for a transaction
pub const MAX_TX_INPUTS: u64 = 2048;

/// Maximum outputs for a transaction
pub const MAX_TX_OUTPUTS: u64 = 500; // wallet uses 500 as max

/// Maximum kernels for a transaction
pub const MAX_TX_KERNELS: u64 = 2048;

/// Whether a block exceeds the maximum acceptable weight
pub fn exceeds_weight(input_len: usize, output_len: usize, kernel_len: usize) -> bool {
	input_len * BLOCK_INPUT_WEIGHT + output_len * BLOCK_OUTPUT_WEIGHT
		+ kernel_len * BLOCK_KERNEL_WEIGHT > MAX_BLOCK_WEIGHT || input_len > MAX_BLOCK_INPUTS
}

/// Fork every 250,000 blocks for first 2 years, simple number and just a
/// little less than 6 months.
pub const HARD_FORK_INTERVAL: u64 = 250_000;

/// Check whether the block version is valid at a given height, implements
/// 6 months interval scheduled hard forks for the first 2 years.
pub fn valid_header_version(height: u64, version: u16) -> bool {
	// uncomment below as we go from hard fork to hard fork
	if height <= HARD_FORK_INTERVAL && version == 1 {
		true
	/* } else if height <= 2 * HARD_FORK_INTERVAL && version == 2 {
		true */
	/* } else if height <= 3 * HARD_FORK_INTERVAL && version == 3 {
		true */
	/* } else if height <= 4 * HARD_FORK_INTERVAL && version == 4 {
		true */
	/* } else if height > 4 * HARD_FORK_INTERVAL && version > 4 {
		true */
	} else {
		false
	}
}

/// Time window in blocks to calculate block time median
pub const MEDIAN_TIME_WINDOW: u64 = 11;

/// Index at half the desired median
pub const MEDIAN_TIME_INDEX: u64 = MEDIAN_TIME_WINDOW / 2;

/// Number of blocks used to calculate difficulty adjustments
pub const DIFFICULTY_ADJUST_WINDOW: u64 = 60;

/// Average time span of the difficulty adjustment window
pub const BLOCK_TIME_WINDOW: u64 = DIFFICULTY_ADJUST_WINDOW * BLOCK_TIME_SEC;

/// Maximum size time window used for difficulty adjustments
pub const UPPER_TIME_BOUND: u64 = BLOCK_TIME_WINDOW * 2;

/// Minimum size time window used for difficulty adjustments
pub const LOWER_TIME_BOUND: u64 = BLOCK_TIME_WINDOW / 2;

/// Dampening factor to use for difficulty adjustment
pub const DAMP_FACTOR: u64 = 3;

/// The initial difficulty at launch. This should be over-estimated
/// and difficulty should come down at launch rather than up
/// Currently grossly over-estimated at 10% of current
/// ethereum GPUs (assuming 1GPU can solve a block at diff 1
/// in one block interval)
pub const INITIAL_DIFFICULTY: u64 = 1_000_000;

/// Consensus errors
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Error {
	/// Inputs/outputs/kernels must be sorted lexicographically.
	SortError,
}

/// Error when computing the next difficulty adjustment.
#[derive(Debug, Clone)]
pub struct TargetError(pub String);

impl fmt::Display for TargetError {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "Error computing new difficulty: {}", self.0)
	}
}

/// Computes the proof-of-work difficulty that the next block should comply
/// with. Takes an iterator over past blocks, from latest (highest height) to
/// oldest (lowest height). The iterator produces pairs of timestamp and
/// difficulty for each block.
///
/// The difficulty calculation is based on both Digishield and GravityWave
/// family of difficulty computation, coming to something very close to Zcash.
/// The reference difficulty is an average of the difficulty over a window of
/// DIFFICULTY_ADJUST_WINDOW blocks. The corresponding timespan is calculated
/// by using the difference between the median timestamps at the beginning
/// and the end of the window.
pub fn next_difficulty<T>(cursor: T) -> Result<Difficulty, TargetError>
where
	T: IntoIterator<Item = Result<(u64, Difficulty), TargetError>>,
{
	// Create vector of difficulty data running from earliest
	// to latest, and pad with simulated pre-genesis data to allow earlier
	// adjustment if there isn't enough window data
	// length will be DIFFICULTY_ADJUST_WINDOW+MEDIAN_TIME_WINDOW
	let diff_data = global::difficulty_data_to_vector(cursor);

	// Obtain the median window for the earlier time period
	// the first MEDIAN_TIME_WINDOW elements
	let mut window_earliest: Vec<u64> = diff_data
		.iter()
		.take(MEDIAN_TIME_WINDOW as usize)
		.map(|n| n.clone().unwrap().0)
		.collect();
	// pick median
	window_earliest.sort();
	let earliest_ts = window_earliest[MEDIAN_TIME_INDEX as usize];

	// Obtain the median window for the latest time period
	// i.e. the last  MEDIAN_TIME_WINDOW elements
	let mut window_latest: Vec<u64> = diff_data
		.iter()
		.skip(DIFFICULTY_ADJUST_WINDOW as usize)
		.map(|n| n.clone().unwrap().0)
		.collect();
	// pick median
	window_latest.sort();
	let latest_ts = window_latest[MEDIAN_TIME_INDEX as usize];

	// median time delta
	let ts_delta = latest_ts - earliest_ts;

	// Get the difficulty sum of the last DIFFICULTY_ADJUST_WINDOW elements
	let diff_sum = diff_data
		.iter()
		.skip(MEDIAN_TIME_WINDOW as usize)
		.fold(0, |sum, d| sum + d.clone().unwrap().1.to_num());

	// Apply dampening except when difficulty is near 1
	let ts_damp = if diff_sum < DAMP_FACTOR * DIFFICULTY_ADJUST_WINDOW {
		ts_delta
	} else {
		(1 * ts_delta + (DAMP_FACTOR - 1) * BLOCK_TIME_WINDOW) / DAMP_FACTOR
	};

	// Apply time bounds
	let adj_ts = if ts_damp < LOWER_TIME_BOUND {
		LOWER_TIME_BOUND
	} else if ts_damp > UPPER_TIME_BOUND {
		UPPER_TIME_BOUND
	} else {
		ts_damp
	};

	// AVOID BREAKING CONSENSUS FOR NOW WITH OLD DOUBLE TRUNCATION CALC
	let difficulty = (diff_sum / DIFFICULTY_ADJUST_WINDOW) * BLOCK_TIME_WINDOW / adj_ts;
	// EVENTUALLY BREAK CONSENSUS WITH THIS IMPROVED SINGLE TRUNCATION DIFF CALC
	// let difficulty = diff_sum * BLOCK_TIME_SEC / adj_ts;

	Ok(Difficulty::from_num(max(difficulty, 1)))
}

/// Consensus rule that collections of items are sorted lexicographically.
pub trait VerifySortOrder<T> {
	/// Verify a collection of items is sorted as required.
	fn verify_sort_order(&self) -> Result<(), Error>;
}
<commit_msg>Less truncation in difficulty calculation (#1116)<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.

//! All the rules required for a cryptocurrency to have reach consensus across
//! the whole network are complex and hard to completely isolate. Some can be
//! simple parameters (like block reward), others complex algorithms (like
//! Merkle sum trees or reorg rules). However, as long as they're simple
//! enough, consensus-relevant constants and short functions should be kept
//! here.

use std::cmp::max;
use std::fmt;

use core::target::Difficulty;
use global;

/// A grin is divisible to 10^9, following the SI prefixes
pub const GRIN_BASE: u64 = 1_000_000_000;
/// Milligrin, a thousand of a grin
pub const MILLI_GRIN: u64 = GRIN_BASE / 1_000;
/// Microgrin, a thousand of a milligrin
pub const MICRO_GRIN: u64 = MILLI_GRIN / 1_000;
/// Nanogrin, smallest unit, takes a billion to make a grin
pub const NANO_GRIN: u64 = 1;

/// The block subsidy amount, one grin per second on average
pub const REWARD: u64 = 60 * GRIN_BASE;

/// Actual block reward for a given total fee amount
pub fn reward(fee: u64) -> u64 {
	REWARD + fee
}

/// Number of blocks before a coinbase matures and can be spent
pub const COINBASE_MATURITY: u64 = 1_000;

/// Block interval, in seconds, the network will tune its next_target for. Note
/// that we may reduce this value in the future as we get more data on mining
/// with Cuckoo Cycle, networks improve and block propagation is optimized
/// (adjusting the reward accordingly).
pub const BLOCK_TIME_SEC: u64 = 60;

/// Cuckoo-cycle proof size (cycle length)
pub const PROOFSIZE: usize = 42;

/// Default Cuckoo Cycle size shift used for mining and validating.
pub const DEFAULT_SIZESHIFT: u8 = 30;

/// Default Cuckoo Cycle easiness, high enough to have good likeliness to find
/// a solution.
pub const EASINESS: u32 = 50;

/// Default number of blocks in the past when cross-block cut-through will start
/// happening. Needs to be long enough to not overlap with a long reorg.
/// Rational
/// behind the value is the longest bitcoin fork was about 30 blocks, so 5h. We
/// add an order of magnitude to be safe and round to 48h of blocks to make it
/// easier to reason about.
pub const CUT_THROUGH_HORIZON: u32 = 48 * 3600 / (BLOCK_TIME_SEC as u32);

/// The maximum size we're willing to accept for any message. Enforced by the
/// peer-to-peer networking layer only for DoS protection.
pub const MAX_MSG_LEN: u64 = 20_000_000;

/// Weight of an input when counted against the max block weight capacity
pub const BLOCK_INPUT_WEIGHT: usize = 1;

/// Weight of an output when counted against the max block weight capacity
pub const BLOCK_OUTPUT_WEIGHT: usize = 10;

/// Weight of a kernel when counted against the max block weight capacity
pub const BLOCK_KERNEL_WEIGHT: usize = 2;

/// Total maximum block weight
pub const MAX_BLOCK_WEIGHT: usize = 80_000;

/// Maximum inputs for a block (issue#261)
/// Hundreds of inputs + 1 output might be slow to validate (issue#258)
pub const MAX_BLOCK_INPUTS: usize = 300_000; // soft fork down when too_high

/// Maximum inputs for a transaction
pub const MAX_TX_INPUTS: u64 = 2048;

/// Maximum outputs for a transaction
pub const MAX_TX_OUTPUTS: u64 = 500; // wallet uses 500 as max

/// Maximum kernels for a transaction
pub const MAX_TX_KERNELS: u64 = 2048;

/// Whether a block exceeds the maximum acceptable weight
pub fn exceeds_weight(input_len: usize, output_len: usize, kernel_len: usize) -> bool {
	input_len * BLOCK_INPUT_WEIGHT + output_len * BLOCK_OUTPUT_WEIGHT
		+ kernel_len * BLOCK_KERNEL_WEIGHT > MAX_BLOCK_WEIGHT || input_len > MAX_BLOCK_INPUTS
}

/// Fork every 250,000 blocks for first 2 years, simple number and just a
/// little less than 6 months.
pub const HARD_FORK_INTERVAL: u64 = 250_000;

/// Check whether the block version is valid at a given height, implements
/// 6 months interval scheduled hard forks for the first 2 years.
pub fn valid_header_version(height: u64, version: u16) -> bool {
	// uncomment below as we go from hard fork to hard fork
	if height <= HARD_FORK_INTERVAL && version == 1 {
		true
	/* } else if height <= 2 * HARD_FORK_INTERVAL && version == 2 {
		true */
	/* } else if height <= 3 * HARD_FORK_INTERVAL && version == 3 {
		true */
	/* } else if height <= 4 * HARD_FORK_INTERVAL && version == 4 {
		true */
	/* } else if height > 4 * HARD_FORK_INTERVAL && version > 4 {
		true */
	} else {
		false
	}
}

/// Time window in blocks to calculate block time median
pub const MEDIAN_TIME_WINDOW: u64 = 11;

/// Index at half the desired median
pub const MEDIAN_TIME_INDEX: u64 = MEDIAN_TIME_WINDOW / 2;

/// Number of blocks used to calculate difficulty adjustments
pub const DIFFICULTY_ADJUST_WINDOW: u64 = 60;

/// Average time span of the difficulty adjustment window
pub const BLOCK_TIME_WINDOW: u64 = DIFFICULTY_ADJUST_WINDOW * BLOCK_TIME_SEC;

/// Maximum size time window used for difficulty adjustments
pub const UPPER_TIME_BOUND: u64 = BLOCK_TIME_WINDOW * 2;

/// Minimum size time window used for difficulty adjustments
pub const LOWER_TIME_BOUND: u64 = BLOCK_TIME_WINDOW / 2;

/// Dampening factor to use for difficulty adjustment
pub const DAMP_FACTOR: u64 = 3;

/// The initial difficulty at launch. This should be over-estimated
/// and difficulty should come down at launch rather than up
/// Currently grossly over-estimated at 10% of current
/// ethereum GPUs (assuming 1GPU can solve a block at diff 1
/// in one block interval)
pub const INITIAL_DIFFICULTY: u64 = 1_000_000;

/// Consensus errors
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Error {
	/// Inputs/outputs/kernels must be sorted lexicographically.
	SortError,
}

/// Error when computing the next difficulty adjustment.
#[derive(Debug, Clone)]
pub struct TargetError(pub String);

impl fmt::Display for TargetError {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "Error computing new difficulty: {}", self.0)
	}
}

/// Computes the proof-of-work difficulty that the next block should comply
/// with. Takes an iterator over past blocks, from latest (highest height) to
/// oldest (lowest height). The iterator produces pairs of timestamp and
/// difficulty for each block.
///
/// The difficulty calculation is based on both Digishield and GravityWave
/// family of difficulty computation, coming to something very close to Zcash.
/// The reference difficulty is an average of the difficulty over a window of
/// DIFFICULTY_ADJUST_WINDOW blocks. The corresponding timespan is calculated
/// by using the difference between the median timestamps at the beginning
/// and the end of the window.
pub fn next_difficulty<T>(cursor: T) -> Result<Difficulty, TargetError>
where
	T: IntoIterator<Item = Result<(u64, Difficulty), TargetError>>,
{
	// Create vector of difficulty data running from earliest
	// to latest, and pad with simulated pre-genesis data to allow earlier
	// adjustment if there isn't enough window data
	// length will be DIFFICULTY_ADJUST_WINDOW+MEDIAN_TIME_WINDOW
	let diff_data = global::difficulty_data_to_vector(cursor);

	// Obtain the median window for the earlier time period
	// the first MEDIAN_TIME_WINDOW elements
	let mut window_earliest: Vec<u64> = diff_data
		.iter()
		.take(MEDIAN_TIME_WINDOW as usize)
		.map(|n| n.clone().unwrap().0)
		.collect();
	// pick median
	window_earliest.sort();
	let earliest_ts = window_earliest[MEDIAN_TIME_INDEX as usize];

	// Obtain the median window for the latest time period
	// i.e. the last  MEDIAN_TIME_WINDOW elements
	let mut window_latest: Vec<u64> = diff_data
		.iter()
		.skip(DIFFICULTY_ADJUST_WINDOW as usize)
		.map(|n| n.clone().unwrap().0)
		.collect();
	// pick median
	window_latest.sort();
	let latest_ts = window_latest[MEDIAN_TIME_INDEX as usize];

	// median time delta
	let ts_delta = latest_ts - earliest_ts;

	// Get the difficulty sum of the last DIFFICULTY_ADJUST_WINDOW elements
	let diff_sum = diff_data
		.iter()
		.skip(MEDIAN_TIME_WINDOW as usize)
		.fold(0, |sum, d| sum + d.clone().unwrap().1.to_num());

	// Apply dampening except when difficulty is near 1
	let ts_damp = if diff_sum < DAMP_FACTOR * DIFFICULTY_ADJUST_WINDOW {
		ts_delta
	} else {
		(1 * ts_delta + (DAMP_FACTOR - 1) * BLOCK_TIME_WINDOW) / DAMP_FACTOR
	};

	// Apply time bounds
	let adj_ts = if ts_damp < LOWER_TIME_BOUND {
		LOWER_TIME_BOUND
	} else if ts_damp > UPPER_TIME_BOUND {
		UPPER_TIME_BOUND
	} else {
		ts_damp
	};

	let difficulty = diff_sum * BLOCK_TIME_SEC / adj_ts;

	Ok(Difficulty::from_num(max(difficulty, 1)))
}

/// Consensus rule that collections of items are sorted lexicographically.
pub trait VerifySortOrder<T> {
	/// Verify a collection of items is sorted as required.
	fn verify_sort_order(&self) -> Result<(), Error>;
}
<|endoftext|>