<commit_before>//! A JSON emitter for errors.
//!
//! This works by converting errors to a simplified structural format (see the
//! structs at the start of the file) and then serializing them. These should
//! contain as much information about the error as possible.
//!
//! The format of the JSON output should be considered *unstable*. For now the
//! structs at the end of this file (Diagnostic*) specify the error format.

// FIXME: spec the JSON output properly.

use rustc_span::source_map::{FilePathMapping, SourceMap};

use crate::emitter::{Emitter, HumanReadableErrorType};
use crate::registry::Registry;
use crate::{Applicability, DiagnosticId};
use crate::{CodeSuggestion, SubDiagnostic};

use rustc_data_structures::sync::Lrc;
use rustc_span::hygiene::ExpnData;
use rustc_span::{MultiSpan, Span, SpanLabel};
use std::io::{self, Write};
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::vec;

use rustc_serialize::json::{as_json, as_pretty_json};

#[cfg(test)]
mod tests;

pub struct JsonEmitter {
    dst: Box<dyn Write + Send>,
    registry: Option<Registry>,
    sm: Lrc<SourceMap>,
    pretty: bool,
    ui_testing: bool,
    json_rendered: HumanReadableErrorType,
    macro_backtrace: bool,
}

impl JsonEmitter {
    pub fn stderr(
        registry: Option<Registry>,
        source_map: Lrc<SourceMap>,
        pretty: bool,
        json_rendered: HumanReadableErrorType,
        macro_backtrace: bool,
    ) -> JsonEmitter {
        JsonEmitter {
            dst: Box::new(io::stderr()),
            registry,
            sm: source_map,
            pretty,
            ui_testing: false,
            json_rendered,
            macro_backtrace,
        }
    }

    pub fn basic(
        pretty: bool,
        json_rendered: HumanReadableErrorType,
        macro_backtrace: bool,
    ) -> JsonEmitter {
        let file_path_mapping = FilePathMapping::empty();
        JsonEmitter::stderr(
            None,
            Lrc::new(SourceMap::new(file_path_mapping)),
            pretty,
            json_rendered,
            macro_backtrace,
        )
    }

    pub fn new(
        dst: Box<dyn Write + Send>,
        registry: Option<Registry>,
        source_map: Lrc<SourceMap>,
        pretty: bool,
        json_rendered: HumanReadableErrorType,
        macro_backtrace: bool,
    ) -> JsonEmitter {
        JsonEmitter {
            dst,
            registry,
            sm: source_map,
            pretty,
            ui_testing: false,
            json_rendered,
            macro_backtrace,
        }
    }

    pub fn ui_testing(self, ui_testing: bool) -> Self {
        Self { ui_testing, ..self }
    }
}

impl Emitter for JsonEmitter {
    fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) {
        let data = Diagnostic::from_errors_diagnostic(diag, self);
        let result = if self.pretty {
            writeln!(&mut self.dst, "{}", as_pretty_json(&data))
        } else {
            writeln!(&mut self.dst, "{}", as_json(&data))
        };
        if let Err(e) = result {
            panic!("failed to print diagnostics: {:?}", e);
        }
    }

    fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) {
        let data = ArtifactNotification { artifact: path, emit: artifact_type };
        let result = if self.pretty {
            writeln!(&mut self.dst, "{}", as_pretty_json(&data))
        } else {
            writeln!(&mut self.dst, "{}", as_json(&data))
        };
        if let Err(e) = result {
            panic!("failed to print notification: {:?}", e);
        }
    }

    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
        Some(&self.sm)
    }

    fn should_show_explain(&self) -> bool {
        match self.json_rendered {
            HumanReadableErrorType::Short(_) => false,
            _ => true,
        }
    }
}

// The following data types are provided just for serialisation.

#[derive(RustcEncodable)]
struct Diagnostic {
    /// The primary error message.
    message: String,
    code: Option<DiagnosticCode>,
    /// "error: internal compiler error", "error", "warning", "note", "help".
    level: &'static str,
    spans: Vec<DiagnosticSpan>,
    /// Associated diagnostic messages.
    children: Vec<Diagnostic>,
    /// The message as rustc would render it.
    rendered: Option<String>,
}

#[derive(RustcEncodable)]
struct DiagnosticSpan {
    file_name: String,
    byte_start: u32,
    byte_end: u32,
    /// 1-based.
    line_start: usize,
    line_end: usize,
    /// 1-based, character offset.
    column_start: usize,
    column_end: usize,
    /// Is this a "primary" span -- meaning the point, or one of the points,
    /// where the error occurred?
    is_primary: bool,
    /// Source text from the start of line_start to the end of line_end.
    text: Vec<DiagnosticSpanLine>,
    /// Label that should be placed at this location (if any)
    label: Option<String>,
    /// If we are suggesting a replacement, this will contain text
    /// that should be sliced in atop this span.
    suggested_replacement: Option<String>,
    /// If the suggestion is approximate
    suggestion_applicability: Option<Applicability>,
    /// Macro invocations that created the code at this span, if any.
    expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
}

#[derive(RustcEncodable)]
struct DiagnosticSpanLine {
    text: String,

    /// 1-based, character offset in self.text.
    highlight_start: usize,

    highlight_end: usize,
}

#[derive(RustcEncodable)]
struct DiagnosticSpanMacroExpansion {
    /// span where macro was applied to generate this code; note that
    /// this may itself derive from a macro (if
    /// `span.expansion.is_some()`)
    span: DiagnosticSpan,

    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
    macro_decl_name: String,

    /// span where macro was defined (if known)
    def_site_span: DiagnosticSpan,
}

#[derive(RustcEncodable)]
struct DiagnosticCode {
    /// The code itself.
    code: String,
    /// An explanation for the code.
    explanation: Option<&'static str>,
}

#[derive(RustcEncodable)]
struct ArtifactNotification<'a> {
    /// The path of the artifact.
    artifact: &'a Path,
    /// What kind of artifact we're emitting.
    emit: &'a str,
}

impl Diagnostic {
    fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
        let sugg = diag.suggestions.iter().map(|sugg| Diagnostic {
            message: sugg.msg.clone(),
            code: None,
            level: "help",
            spans: DiagnosticSpan::from_suggestion(sugg, je),
            children: vec![],
            rendered: None,
        });

        // generate regular command line output and store it in the json

        // A threadsafe buffer for writing.
        #[derive(Default, Clone)]
        struct BufWriter(Arc<Mutex<Vec<u8>>>);

        impl Write for BufWriter {
            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
                self.0.lock().unwrap().write(buf)
            }
            fn flush(&mut self) -> io::Result<()> {
                self.0.lock().unwrap().flush()
            }
        }
        let buf = BufWriter::default();
        let output = buf.clone();
        je.json_rendered
            .new_emitter(Box::new(buf), Some(je.sm.clone()), false, None, je.macro_backtrace)
            .ui_testing(je.ui_testing)
            .emit_diagnostic(diag);
        let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
        let output = String::from_utf8(output).unwrap();

        Diagnostic {
            message: diag.message(),
            code: DiagnosticCode::map_opt_string(diag.code.clone(), je),
            level: diag.level.to_str(),
            spans: DiagnosticSpan::from_multispan(&diag.span, je),
            children: diag
                .children
                .iter()
                .map(|c| Diagnostic::from_sub_diagnostic(c, je))
                .chain(sugg)
                .collect(),
            rendered: Some(output),
        }
    }

    fn from_sub_diagnostic(diag: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic {
        Diagnostic {
            message: diag.message(),
            code: None,
            level: diag.level.to_str(),
            spans: diag
                .render_span
                .as_ref()
                .map(|sp| DiagnosticSpan::from_multispan(sp, je))
                .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)),
            children: vec![],
            rendered: None,
        }
    }
}

impl DiagnosticSpan {
    fn from_span_label(
        span: SpanLabel,
        suggestion: Option<(&String, Applicability)>,
        je: &JsonEmitter,
    ) -> DiagnosticSpan {
        Self::from_span_etc(span.span, span.is_primary, span.label, suggestion, je)
    }

    fn from_span_etc(
        span: Span,
        is_primary: bool,
        label: Option<String>,
        suggestion: Option<(&String, Applicability)>,
        je: &JsonEmitter,
    ) -> DiagnosticSpan {
        // obtain the full backtrace from the `macro_backtrace`
        // helper; in some ways, it'd be better to expand the
        // backtrace ourselves, but the `macro_backtrace` helper makes
        // some decision, such as dropping some frames, and I don't
        // want to duplicate that logic here.
        let backtrace = span.macro_backtrace();
        DiagnosticSpan::from_span_full(span, is_primary, label, suggestion, backtrace, je)
    }

    fn from_span_full(
        span: Span,
        is_primary: bool,
        label: Option<String>,
        suggestion: Option<(&String, Applicability)>,
        mut backtrace: impl Iterator<Item = ExpnData>,
        je: &JsonEmitter,
    ) -> DiagnosticSpan {
        let start = je.sm.lookup_char_pos(span.lo());
        let end = je.sm.lookup_char_pos(span.hi());
        let backtrace_step = backtrace.next().map(|bt| {
            let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je);
            let def_site_span =
                Self::from_span_full(bt.def_site, false, None, None, vec![].into_iter(), je);
            Box::new(DiagnosticSpanMacroExpansion {
                span: call_site,
                macro_decl_name: bt.kind.descr(),
                def_site_span,
            })
        });

        DiagnosticSpan {
            file_name: start.file.name.to_string(),
            byte_start: start.file.original_relative_byte_pos(span.lo()).0,
            byte_end: start.file.original_relative_byte_pos(span.hi()).0,
            line_start: start.line,
            line_end: end.line,
            column_start: start.col.0 + 1,
            column_end: end.col.0 + 1,
            is_primary,
            text: DiagnosticSpanLine::from_span(span, je),
            suggested_replacement: suggestion.map(|x| x.0.clone()),
            suggestion_applicability: suggestion.map(|x| x.1),
            expansion: backtrace_step,
            label,
        }
    }

    fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
        msp.span_labels()
            .into_iter()
            .map(|span_str| Self::from_span_label(span_str, None, je))
            .collect()
    }

    fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
        suggestion
            .substitutions
            .iter()
            .flat_map(|substitution| {
                substitution.parts.iter().map(move |suggestion_inner| {
                    let span_label =
                        SpanLabel { span: suggestion_inner.span, is_primary: true, label: None };
                    DiagnosticSpan::from_span_label(
                        span_label,
                        Some((&suggestion_inner.snippet, suggestion.applicability)),
                        je,
                    )
                })
            })
            .collect()
    }
}

impl DiagnosticSpanLine {
    fn line_from_source_file(
        fm: &rustc_span::SourceFile,
        index: usize,
        h_start: usize,
        h_end: usize,
    ) -> DiagnosticSpanLine {
        DiagnosticSpanLine {
            text: fm.get_line(index).map_or(String::new(), |l| l.into_owned()),
            highlight_start: h_start,
            highlight_end: h_end,
        }
    }

    /// Creates a list of DiagnosticSpanLines from span - each line with any part
    /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the
    /// `span` within the line.
    fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
        je.sm
            .span_to_lines(span)
            .map(|lines| {
                let fm = &*lines.file;
                lines
                    .lines
                    .iter()
                    .map(|line| {
                        DiagnosticSpanLine::line_from_source_file(
                            fm,
                            line.line_index,
                            line.start_col.0 + 1,
                            line.end_col.0 + 1,
                        )
                    })
                    .collect()
            })
            .unwrap_or_else(|_| vec![])
    }
}

impl DiagnosticCode {
    fn map_opt_string(s: Option<DiagnosticId>, je: &JsonEmitter) -> Option<DiagnosticCode> {
        s.map(|s| {
            let s = match s {
                DiagnosticId::Error(s) => s,
                DiagnosticId::Lint(s) => s,
            };
            let explanation =
                je.registry.as_ref().and_then(|registry| registry.find_description(&s));

            DiagnosticCode { code: s, explanation }
        })
    }
}
<commit_msg>perf: Buffer stderr when writing json errors/warnings<commit_after>//! A JSON emitter for errors.
//!
//! This works by converting errors to a simplified structural format (see the
//! structs at the start of the file) and then serializing them. These should
//! contain as much information about the error as possible.
//!
//! The format of the JSON output should be considered *unstable*. For now the
//! structs at the end of this file (Diagnostic*) specify the error format.

// FIXME: spec the JSON output properly.

use rustc_span::source_map::{FilePathMapping, SourceMap};

use crate::emitter::{Emitter, HumanReadableErrorType};
use crate::registry::Registry;
use crate::{Applicability, DiagnosticId};
use crate::{CodeSuggestion, SubDiagnostic};

use rustc_data_structures::sync::Lrc;
use rustc_span::hygiene::ExpnData;
use rustc_span::{MultiSpan, Span, SpanLabel};
use std::io::{self, Write};
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::vec;

use rustc_serialize::json::{as_json, as_pretty_json};

#[cfg(test)]
mod tests;

pub struct JsonEmitter {
    dst: Box<dyn Write + Send>,
    registry: Option<Registry>,
    sm: Lrc<SourceMap>,
    pretty: bool,
    ui_testing: bool,
    json_rendered: HumanReadableErrorType,
    macro_backtrace: bool,
}

impl JsonEmitter {
    pub fn stderr(
        registry: Option<Registry>,
        source_map: Lrc<SourceMap>,
        pretty: bool,
        json_rendered: HumanReadableErrorType,
        macro_backtrace: bool,
    ) -> JsonEmitter {
        JsonEmitter {
            dst: Box::new(io::BufWriter::new(io::stderr())),
            registry,
            sm: source_map,
            pretty,
            ui_testing: false,
            json_rendered,
            macro_backtrace,
        }
    }

    pub fn basic(
        pretty: bool,
        json_rendered: HumanReadableErrorType,
        macro_backtrace: bool,
    ) -> JsonEmitter {
        let file_path_mapping = FilePathMapping::empty();
        JsonEmitter::stderr(
            None,
            Lrc::new(SourceMap::new(file_path_mapping)),
            pretty,
            json_rendered,
            macro_backtrace,
        )
    }

    pub fn new(
        dst: Box<dyn Write + Send>,
        registry: Option<Registry>,
        source_map: Lrc<SourceMap>,
        pretty: bool,
        json_rendered: HumanReadableErrorType,
        macro_backtrace: bool,
    ) -> JsonEmitter {
        JsonEmitter {
            dst,
            registry,
            sm: source_map,
            pretty,
            ui_testing: false,
            json_rendered,
            macro_backtrace,
        }
    }

    pub fn ui_testing(self, ui_testing: bool) -> Self {
        Self { ui_testing, ..self }
    }
}

impl Emitter for JsonEmitter {
    fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) {
        let data = Diagnostic::from_errors_diagnostic(diag, self);
        let result = if self.pretty {
            writeln!(&mut self.dst, "{}", as_pretty_json(&data))
        } else {
            writeln!(&mut self.dst, "{}", as_json(&data))
        };
        if let Err(e) = result {
            panic!("failed to print diagnostics: {:?}", e);
        }
    }

    fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) {
        let data = ArtifactNotification { artifact: path, emit: artifact_type };
        let result = if self.pretty {
            writeln!(&mut self.dst, "{}", as_pretty_json(&data))
        } else {
            writeln!(&mut self.dst, "{}", as_json(&data))
        };
        if let Err(e) = result {
            panic!("failed to print notification: {:?}", e);
        }
    }

    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
        Some(&self.sm)
    }

    fn should_show_explain(&self) -> bool {
        match self.json_rendered {
            HumanReadableErrorType::Short(_) => false,
            _ => true,
        }
    }
}

// The following data types are provided just for serialisation.

#[derive(RustcEncodable)]
struct Diagnostic {
    /// The primary error message.
    message: String,
    code: Option<DiagnosticCode>,
    /// "error: internal compiler error", "error", "warning", "note", "help".
    level: &'static str,
    spans: Vec<DiagnosticSpan>,
    /// Associated diagnostic messages.
    children: Vec<Diagnostic>,
    /// The message as rustc would render it.
    rendered: Option<String>,
}

#[derive(RustcEncodable)]
struct DiagnosticSpan {
    file_name: String,
    byte_start: u32,
    byte_end: u32,
    /// 1-based.
    line_start: usize,
    line_end: usize,
    /// 1-based, character offset.
    column_start: usize,
    column_end: usize,
    /// Is this a "primary" span -- meaning the point, or one of the points,
    /// where the error occurred?
    is_primary: bool,
    /// Source text from the start of line_start to the end of line_end.
    text: Vec<DiagnosticSpanLine>,
    /// Label that should be placed at this location (if any)
    label: Option<String>,
    /// If we are suggesting a replacement, this will contain text
    /// that should be sliced in atop this span.
    suggested_replacement: Option<String>,
    /// If the suggestion is approximate
    suggestion_applicability: Option<Applicability>,
    /// Macro invocations that created the code at this span, if any.
    expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
}

#[derive(RustcEncodable)]
struct DiagnosticSpanLine {
    text: String,

    /// 1-based, character offset in self.text.
    highlight_start: usize,

    highlight_end: usize,
}

#[derive(RustcEncodable)]
struct DiagnosticSpanMacroExpansion {
    /// span where macro was applied to generate this code; note that
    /// this may itself derive from a macro (if
    /// `span.expansion.is_some()`)
    span: DiagnosticSpan,

    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
    macro_decl_name: String,

    /// span where macro was defined (if known)
    def_site_span: DiagnosticSpan,
}

#[derive(RustcEncodable)]
struct DiagnosticCode {
    /// The code itself.
    code: String,
    /// An explanation for the code.
    explanation: Option<&'static str>,
}

#[derive(RustcEncodable)]
struct ArtifactNotification<'a> {
    /// The path of the artifact.
    artifact: &'a Path,
    /// What kind of artifact we're emitting.
    emit: &'a str,
}

impl Diagnostic {
    fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
        let sugg = diag.suggestions.iter().map(|sugg| Diagnostic {
            message: sugg.msg.clone(),
            code: None,
            level: "help",
            spans: DiagnosticSpan::from_suggestion(sugg, je),
            children: vec![],
            rendered: None,
        });

        // generate regular command line output and store it in the json

        // A threadsafe buffer for writing.
        #[derive(Default, Clone)]
        struct BufWriter(Arc<Mutex<Vec<u8>>>);

        impl Write for BufWriter {
            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
                self.0.lock().unwrap().write(buf)
            }
            fn flush(&mut self) -> io::Result<()> {
                self.0.lock().unwrap().flush()
            }
        }
        let buf = BufWriter::default();
        let output = buf.clone();
        je.json_rendered
            .new_emitter(Box::new(buf), Some(je.sm.clone()), false, None, je.macro_backtrace)
            .ui_testing(je.ui_testing)
            .emit_diagnostic(diag);
        let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
        let output = String::from_utf8(output).unwrap();

        Diagnostic {
            message: diag.message(),
            code: DiagnosticCode::map_opt_string(diag.code.clone(), je),
            level: diag.level.to_str(),
            spans: DiagnosticSpan::from_multispan(&diag.span, je),
            children: diag
                .children
                .iter()
                .map(|c| Diagnostic::from_sub_diagnostic(c, je))
                .chain(sugg)
                .collect(),
            rendered: Some(output),
        }
    }

    fn from_sub_diagnostic(diag: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic {
        Diagnostic {
            message: diag.message(),
            code: None,
            level: diag.level.to_str(),
            spans: diag
                .render_span
                .as_ref()
                .map(|sp| DiagnosticSpan::from_multispan(sp, je))
                .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)),
            children: vec![],
            rendered: None,
        }
    }
}

impl DiagnosticSpan {
    fn from_span_label(
        span: SpanLabel,
        suggestion: Option<(&String, Applicability)>,
        je: &JsonEmitter,
    ) -> DiagnosticSpan {
        Self::from_span_etc(span.span, span.is_primary, span.label, suggestion, je)
    }

    fn from_span_etc(
        span: Span,
        is_primary: bool,
        label: Option<String>,
        suggestion: Option<(&String, Applicability)>,
        je: &JsonEmitter,
    ) -> DiagnosticSpan {
        // obtain the full backtrace from the `macro_backtrace`
        // helper; in some ways, it'd be better to expand the
        // backtrace ourselves, but the `macro_backtrace` helper makes
        // some decision, such as dropping some frames, and I don't
        // want to duplicate that logic here.
        let backtrace = span.macro_backtrace();
        DiagnosticSpan::from_span_full(span, is_primary, label, suggestion, backtrace, je)
    }

    fn from_span_full(
        span: Span,
        is_primary: bool,
        label: Option<String>,
        suggestion: Option<(&String, Applicability)>,
        mut backtrace: impl Iterator<Item = ExpnData>,
        je: &JsonEmitter,
    ) -> DiagnosticSpan {
        let start = je.sm.lookup_char_pos(span.lo());
        let end = je.sm.lookup_char_pos(span.hi());
        let backtrace_step = backtrace.next().map(|bt| {
            let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je);
            let def_site_span =
                Self::from_span_full(bt.def_site, false, None, None, vec![].into_iter(), je);
            Box::new(DiagnosticSpanMacroExpansion {
                span: call_site,
                macro_decl_name: bt.kind.descr(),
                def_site_span,
            })
        });

        DiagnosticSpan {
            file_name: start.file.name.to_string(),
            byte_start: start.file.original_relative_byte_pos(span.lo()).0,
            byte_end: start.file.original_relative_byte_pos(span.hi()).0,
            line_start: start.line,
            line_end: end.line,
            column_start: start.col.0 + 1,
            column_end: end.col.0 + 1,
            is_primary,
            text: DiagnosticSpanLine::from_span(span, je),
            suggested_replacement: suggestion.map(|x| x.0.clone()),
            suggestion_applicability: suggestion.map(|x| x.1),
            expansion: backtrace_step,
            label,
        }
    }

    fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
        msp.span_labels()
            .into_iter()
            .map(|span_str| Self::from_span_label(span_str, None, je))
            .collect()
    }

    fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
        suggestion
            .substitutions
            .iter()
            .flat_map(|substitution| {
                substitution.parts.iter().map(move |suggestion_inner| {
                    let span_label =
                        SpanLabel { span: suggestion_inner.span, is_primary: true, label: None };
                    DiagnosticSpan::from_span_label(
                        span_label,
                        Some((&suggestion_inner.snippet, suggestion.applicability)),
                        je,
                    )
                })
            })
            .collect()
    }
}

impl DiagnosticSpanLine {
    fn line_from_source_file(
        fm: &rustc_span::SourceFile,
        index: usize,
        h_start: usize,
        h_end: usize,
    ) -> DiagnosticSpanLine {
        DiagnosticSpanLine {
            text: fm.get_line(index).map_or(String::new(), |l| l.into_owned()),
            highlight_start: h_start,
            highlight_end: h_end,
        }
    }

    /// Creates a list of DiagnosticSpanLines from span - each line with any part
    /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the
    /// `span` within the line.
    fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
        je.sm
            .span_to_lines(span)
            .map(|lines| {
                let fm = &*lines.file;
                lines
                    .lines
                    .iter()
                    .map(|line| {
                        DiagnosticSpanLine::line_from_source_file(
                            fm,
                            line.line_index,
                            line.start_col.0 + 1,
                            line.end_col.0 + 1,
                        )
                    })
                    .collect()
            })
            .unwrap_or_else(|_| vec![])
    }
}

impl DiagnosticCode {
    fn map_opt_string(s: Option<DiagnosticId>, je: &JsonEmitter) -> Option<DiagnosticCode> {
        s.map(|s| {
            let s = match s {
                DiagnosticId::Error(s) => s,
                DiagnosticId::Lint(s) => s,
            };
            let explanation =
                je.registry.as_ref().and_then(|registry| registry.find_description(&s));

            DiagnosticCode { code: s, explanation }
        })
    }
}
<|endoftext|>


<commit_before>//! Condition codes for the Cretonne code generator.
//!
//! A condition code here is an enumerated type that determined how to compare two numbers. There
//! are different rules for comparing integers and floating point numbers, so they use different
//! condition codes.

use std::fmt::{self, Display, Formatter};
use std::str::FromStr;

/// Common traits of condition codes.
pub trait CondCode: Copy {
    /// Get the inverse condition code of `self`.
    ///
    /// The inverse condition code produces the opposite result for all comparisons.
    /// That is, `cmp CC, x, y` is true if and only if `cmp CC.inverse(), x, y` is false.
    fn inverse(self) -> Self;

    /// Get the reversed condition code for `self`.
    ///
    /// The reversed condition code produces the same result as swapping `x` and `y` in the
    /// comparison. That is, `cmp CC, x, y` is the same as `cmp CC.reverse(), y, x`.
    fn reverse(self) -> Self;
}

/// Condition code for comparing integers.
///
/// This condition code is used by the `icmp` instruction to compare integer values. There are
/// separate codes for comparing the integers as signed or unsigned numbers where it makes a
/// difference.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum IntCC {
    /// `==`.
    Equal,
    /// `!=`.
    NotEqual,
    /// Signed `<`.
    SignedLessThan,
    /// Signed `>=`.
    SignedGreaterThanOrEqual,
    /// Signed `>`.
    SignedGreaterThan,
    /// Signed `<=`.
    SignedLessThanOrEqual,
    /// Unsigned `<`.
    UnsignedLessThan,
    /// Unsigned `>=`.
    UnsignedGreaterThanOrEqual,
    /// Unsigned `>`.
    UnsignedGreaterThan,
    /// Unsigned `<=`.
    UnsignedLessThanOrEqual,
}

impl CondCode for IntCC {
    fn inverse(self) -> Self {
        use self::IntCC::*;
        match self {
            Equal => NotEqual,
            NotEqual => Equal,
            SignedLessThan => SignedGreaterThanOrEqual,
            SignedGreaterThanOrEqual => SignedLessThan,
            SignedGreaterThan => SignedLessThanOrEqual,
            SignedLessThanOrEqual => SignedGreaterThan,
            UnsignedLessThan => UnsignedGreaterThanOrEqual,
            UnsignedGreaterThanOrEqual => UnsignedLessThan,
            UnsignedGreaterThan => UnsignedLessThanOrEqual,
            UnsignedLessThanOrEqual => UnsignedGreaterThan,
        }
    }

    fn reverse(self) -> Self {
        use self::IntCC::*;
        match self {
            Equal => Equal,
            NotEqual => NotEqual,
            SignedGreaterThan => SignedLessThan,
            SignedGreaterThanOrEqual => SignedLessThanOrEqual,
            SignedLessThan => SignedGreaterThan,
            SignedLessThanOrEqual => SignedGreaterThanOrEqual,
            UnsignedGreaterThan => UnsignedLessThan,
            UnsignedGreaterThanOrEqual => UnsignedLessThanOrEqual,
            UnsignedLessThan => UnsignedGreaterThan,
            UnsignedLessThanOrEqual => UnsignedGreaterThanOrEqual,
        }
    }
}

impl Display for IntCC {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::IntCC::*;
        f.write_str(match *self {
            Equal => "eq",
            NotEqual => "ne",
            SignedGreaterThan => "sgt",
            SignedGreaterThanOrEqual => "sge",
            SignedLessThan => "slt",
            SignedLessThanOrEqual => "sle",
            UnsignedGreaterThan => "ugt",
            UnsignedGreaterThanOrEqual => "uge",
            UnsignedLessThan => "ult",
            UnsignedLessThanOrEqual => "ule",
        })
    }
}

impl FromStr for IntCC {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::IntCC::*;
        match s {
            "eq" => Ok(Equal),
            "ne" => Ok(NotEqual),
            "sge" => Ok(SignedGreaterThanOrEqual),
            "sgt" => Ok(SignedGreaterThan),
            "sle" => Ok(SignedLessThanOrEqual),
            "slt" => Ok(SignedLessThan),
            "uge" => Ok(UnsignedGreaterThanOrEqual),
            "ugt" => Ok(UnsignedGreaterThan),
            "ule" => Ok(UnsignedLessThanOrEqual),
            "ult" => Ok(UnsignedLessThan),
            _ => Err(()),
        }
    }
}

/// Condition code for comparing floating point numbers.
///
/// This condition code is used by the `fcmp` instruction to compare floating point values. Two
/// IEEE floating point values relate in exactly one of four ways:
///
/// 1. `UN` - unordered when either value is NaN.
/// 2. `EQ` - equal numerical value.
/// 3. `LT` - `x` is less than `y`.
/// 4. `GT` - `x` is greater than `y`.
///
/// Note that `0.0` and `-0.0` relate as `EQ` because they both represent the number 0.
///
/// The condition codes described here are used to produce a single boolean value from the
/// comparison. The 14 condition codes here cover every possible combination of the relation above
/// except the impossible `!UN & !EQ & !LT & !GT` and the always true `UN | EQ | LT | GT`.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum FloatCC {
    /// EQ | LT | GT
    Ordered,
    /// UN
    Unordered,

    /// EQ
    Equal,
    /// The C '!=' operator is the inverse of '==': `NotEqual`.
    /// UN | LT | GT
    NotEqual,
    /// LT | GT
    OrderedNotEqual,
    /// UN | EQ
    UnorderedOrEqual,

    /// LT
    LessThan,
    /// LT | EQ
    LessThanOrEqual,
    /// GT
    GreaterThan,
    /// GT | EQ
    GreaterThanOrEqual,

    /// UN | LT
    UnorderedOrLessThan,
    /// UN | LT | EQ
    UnorderedOrLessThanOrEqual,
    /// UN | GT
    UnorderedOrGreaterThan,
    /// UN | GT | EQ
    UnorderedOrGreaterThanOrEqual,
}

impl CondCode for FloatCC {
    fn inverse(self) -> Self {
        use self::FloatCC::*;
        match self {
            Ordered => Unordered,
            Unordered => Ordered,
            Equal => NotEqual,
            NotEqual => Equal,
            OrderedNotEqual => UnorderedOrEqual,
            UnorderedOrEqual => OrderedNotEqual,
            LessThan => UnorderedOrGreaterThanOrEqual,
            LessThanOrEqual => UnorderedOrGreaterThan,
            GreaterThan => UnorderedOrLessThanOrEqual,
            GreaterThanOrEqual => UnorderedOrLessThan,
            UnorderedOrLessThan => GreaterThanOrEqual,
            UnorderedOrLessThanOrEqual => GreaterThan,
            UnorderedOrGreaterThan => LessThanOrEqual,
            UnorderedOrGreaterThanOrEqual => LessThan,
        }
    }
    fn reverse(self) -> Self {
        use self::FloatCC::*;
        match self {
            Ordered => Ordered,
            Unordered => Unordered,
            Equal => Equal,
            NotEqual => NotEqual,
            OrderedNotEqual => OrderedNotEqual,
            UnorderedOrEqual => UnorderedOrEqual,
            LessThan => GreaterThan,
            LessThanOrEqual => GreaterThanOrEqual,
            GreaterThan => LessThan,
            GreaterThanOrEqual => LessThanOrEqual,
            UnorderedOrLessThan => UnorderedOrGreaterThan,
            UnorderedOrLessThanOrEqual => UnorderedOrGreaterThanOrEqual,
            UnorderedOrGreaterThan => UnorderedOrLessThan,
            UnorderedOrGreaterThanOrEqual => UnorderedOrLessThanOrEqual,
        }
    }
}

impl Display for FloatCC {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::FloatCC::*;
        f.write_str(match *self {
            Ordered => "ord",
            Unordered => "uno",
            Equal => "eq",
            NotEqual => "ne",
            OrderedNotEqual => "one",
            UnorderedOrEqual => "ueq",
            LessThan => "lt",
            LessThanOrEqual => "le",
            GreaterThan => "gt",
            GreaterThanOrEqual => "ge",
            UnorderedOrLessThan => "ult",
            UnorderedOrLessThanOrEqual => "ule",
            UnorderedOrGreaterThan => "ugt",
            UnorderedOrGreaterThanOrEqual => "uge",
        })
    }
}

impl FromStr for FloatCC {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::FloatCC::*;
        match s {
            "ord" => Ok(Ordered),
            "uno" => Ok(Unordered),
            "eq" => Ok(Equal),
            "ne" => Ok(NotEqual),
            "one" => Ok(OrderedNotEqual),
            "ueq" => Ok(UnorderedOrEqual),
            "lt" => Ok(LessThan),
            "le" => Ok(LessThanOrEqual),
            "gt" => Ok(GreaterThan),
            "ge" => Ok(GreaterThanOrEqual),
            "ult" => Ok(UnorderedOrLessThan),
            "ule" => Ok(UnorderedOrLessThanOrEqual),
            "ugt" => Ok(UnorderedOrGreaterThan),
            "uge" => Ok(UnorderedOrGreaterThanOrEqual),
            _ => Err(()),
        }
    }
}

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

    static INT_ALL: [IntCC; 10] = [
        IntCC::Equal,
        IntCC::NotEqual,
        IntCC::SignedLessThan,
        IntCC::SignedGreaterThanOrEqual,
        IntCC::SignedGreaterThan,
        IntCC::SignedLessThanOrEqual,
        IntCC::UnsignedLessThan,
        IntCC::UnsignedGreaterThanOrEqual,
        IntCC::UnsignedGreaterThan,
        IntCC::UnsignedLessThanOrEqual,
    ];

    #[test]
    fn int_inverse() {
        for r in &INT_ALL {
            let cc = *r;
            let inv = cc.inverse();
            assert!(cc != inv);
            assert_eq!(inv.inverse(), cc);
        }
    }

    #[test]
    fn int_reverse() {
        for r in &INT_ALL {
            let cc = *r;
            let rev = cc.reverse();
            assert_eq!(rev.reverse(), cc);
        }
    }

    #[test]
    fn int_display() {
        for r in &INT_ALL {
            let cc = *r;
            assert_eq!(cc.to_string().parse(), Ok(cc));
        }
        assert_eq!("bogus".parse::<IntCC>(), Err(()));
    }

    static FLOAT_ALL: [FloatCC; 14] = [
        FloatCC::Ordered,
        FloatCC::Unordered,
        FloatCC::Equal,
        FloatCC::NotEqual,
        FloatCC::OrderedNotEqual,
        FloatCC::UnorderedOrEqual,
        FloatCC::LessThan,
        FloatCC::LessThanOrEqual,
        FloatCC::GreaterThan,
        FloatCC::GreaterThanOrEqual,
        FloatCC::UnorderedOrLessThan,
        FloatCC::UnorderedOrLessThanOrEqual,
        FloatCC::UnorderedOrGreaterThan,
        FloatCC::UnorderedOrGreaterThanOrEqual,
    ];

    #[test]
    fn float_inverse() {
        for r in &FLOAT_ALL {
            let cc = *r;
            let inv = cc.inverse();
            assert!(cc != inv);
            assert_eq!(inv.inverse(), cc);
        }
    }

    #[test]
    fn float_reverse() {
        for r in &FLOAT_ALL {
            let cc = *r;
            let rev = cc.reverse();
            assert_eq!(rev.reverse(), cc);
        }
    }

    #[test]
    fn float_display() {
        for r in &FLOAT_ALL {
            let cc = *r;
            assert_eq!(cc.to_string().parse(), Ok(cc));
        }
        assert_eq!("bogus".parse::<FloatCC>(), Err(()));
    }
}
<commit_msg>Mark CondCode's functions #[must_use].<commit_after>//! Condition codes for the Cretonne code generator.
//!
//! A condition code here is an enumerated type that determined how to compare two numbers. There
//! are different rules for comparing integers and floating point numbers, so they use different
//! condition codes.

use std::fmt::{self, Display, Formatter};
use std::str::FromStr;

/// Common traits of condition codes.
pub trait CondCode: Copy {
    /// Get the inverse condition code of `self`.
    ///
    /// The inverse condition code produces the opposite result for all comparisons.
    /// That is, `cmp CC, x, y` is true if and only if `cmp CC.inverse(), x, y` is false.
    #[must_use]
    fn inverse(self) -> Self;

    /// Get the reversed condition code for `self`.
    ///
    /// The reversed condition code produces the same result as swapping `x` and `y` in the
    /// comparison. That is, `cmp CC, x, y` is the same as `cmp CC.reverse(), y, x`.
    #[must_use]
    fn reverse(self) -> Self;
}

/// Condition code for comparing integers.
///
/// This condition code is used by the `icmp` instruction to compare integer values. There are
/// separate codes for comparing the integers as signed or unsigned numbers where it makes a
/// difference.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum IntCC {
    /// `==`.
    Equal,
    /// `!=`.
    NotEqual,
    /// Signed `<`.
    SignedLessThan,
    /// Signed `>=`.
    SignedGreaterThanOrEqual,
    /// Signed `>`.
    SignedGreaterThan,
    /// Signed `<=`.
    SignedLessThanOrEqual,
    /// Unsigned `<`.
    UnsignedLessThan,
    /// Unsigned `>=`.
    UnsignedGreaterThanOrEqual,
    /// Unsigned `>`.
    UnsignedGreaterThan,
    /// Unsigned `<=`.
    UnsignedLessThanOrEqual,
}

impl CondCode for IntCC {
    fn inverse(self) -> Self {
        use self::IntCC::*;
        match self {
            Equal => NotEqual,
            NotEqual => Equal,
            SignedLessThan => SignedGreaterThanOrEqual,
            SignedGreaterThanOrEqual => SignedLessThan,
            SignedGreaterThan => SignedLessThanOrEqual,
            SignedLessThanOrEqual => SignedGreaterThan,
            UnsignedLessThan => UnsignedGreaterThanOrEqual,
            UnsignedGreaterThanOrEqual => UnsignedLessThan,
            UnsignedGreaterThan => UnsignedLessThanOrEqual,
            UnsignedLessThanOrEqual => UnsignedGreaterThan,
        }
    }

    fn reverse(self) -> Self {
        use self::IntCC::*;
        match self {
            Equal => Equal,
            NotEqual => NotEqual,
            SignedGreaterThan => SignedLessThan,
            SignedGreaterThanOrEqual => SignedLessThanOrEqual,
            SignedLessThan => SignedGreaterThan,
            SignedLessThanOrEqual => SignedGreaterThanOrEqual,
            UnsignedGreaterThan => UnsignedLessThan,
            UnsignedGreaterThanOrEqual => UnsignedLessThanOrEqual,
            UnsignedLessThan => UnsignedGreaterThan,
            UnsignedLessThanOrEqual => UnsignedGreaterThanOrEqual,
        }
    }
}

impl Display for IntCC {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::IntCC::*;
        f.write_str(match *self {
            Equal => "eq",
            NotEqual => "ne",
            SignedGreaterThan => "sgt",
            SignedGreaterThanOrEqual => "sge",
            SignedLessThan => "slt",
            SignedLessThanOrEqual => "sle",
            UnsignedGreaterThan => "ugt",
            UnsignedGreaterThanOrEqual => "uge",
            UnsignedLessThan => "ult",
            UnsignedLessThanOrEqual => "ule",
        })
    }
}

impl FromStr for IntCC {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::IntCC::*;
        match s {
            "eq" => Ok(Equal),
            "ne" => Ok(NotEqual),
            "sge" => Ok(SignedGreaterThanOrEqual),
            "sgt" => Ok(SignedGreaterThan),
            "sle" => Ok(SignedLessThanOrEqual),
            "slt" => Ok(SignedLessThan),
            "uge" => Ok(UnsignedGreaterThanOrEqual),
            "ugt" => Ok(UnsignedGreaterThan),
            "ule" => Ok(UnsignedLessThanOrEqual),
            "ult" => Ok(UnsignedLessThan),
            _ => Err(()),
        }
    }
}

/// Condition code for comparing floating point numbers.
///
/// This condition code is used by the `fcmp` instruction to compare floating point values. Two
/// IEEE floating point values relate in exactly one of four ways:
///
/// 1. `UN` - unordered when either value is NaN.
/// 2. `EQ` - equal numerical value.
/// 3. `LT` - `x` is less than `y`.
/// 4. `GT` - `x` is greater than `y`.
///
/// Note that `0.0` and `-0.0` relate as `EQ` because they both represent the number 0.
///
/// The condition codes described here are used to produce a single boolean value from the
/// comparison. The 14 condition codes here cover every possible combination of the relation above
/// except the impossible `!UN & !EQ & !LT & !GT` and the always true `UN | EQ | LT | GT`.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum FloatCC {
    /// EQ | LT | GT
    Ordered,
    /// UN
    Unordered,

    /// EQ
    Equal,
    /// The C '!=' operator is the inverse of '==': `NotEqual`.
    /// UN | LT | GT
    NotEqual,
    /// LT | GT
    OrderedNotEqual,
    /// UN | EQ
    UnorderedOrEqual,

    /// LT
    LessThan,
    /// LT | EQ
    LessThanOrEqual,
    /// GT
    GreaterThan,
    /// GT | EQ
    GreaterThanOrEqual,

    /// UN | LT
    UnorderedOrLessThan,
    /// UN | LT | EQ
    UnorderedOrLessThanOrEqual,
    /// UN | GT
    UnorderedOrGreaterThan,
    /// UN | GT | EQ
    UnorderedOrGreaterThanOrEqual,
}

impl CondCode for FloatCC {
    fn inverse(self) -> Self {
        use self::FloatCC::*;
        match self {
            Ordered => Unordered,
            Unordered => Ordered,
            Equal => NotEqual,
            NotEqual => Equal,
            OrderedNotEqual => UnorderedOrEqual,
            UnorderedOrEqual => OrderedNotEqual,
            LessThan => UnorderedOrGreaterThanOrEqual,
            LessThanOrEqual => UnorderedOrGreaterThan,
            GreaterThan => UnorderedOrLessThanOrEqual,
            GreaterThanOrEqual => UnorderedOrLessThan,
            UnorderedOrLessThan => GreaterThanOrEqual,
            UnorderedOrLessThanOrEqual => GreaterThan,
            UnorderedOrGreaterThan => LessThanOrEqual,
            UnorderedOrGreaterThanOrEqual => LessThan,
        }
    }
    fn reverse(self) -> Self {
        use self::FloatCC::*;
        match self {
            Ordered => Ordered,
            Unordered => Unordered,
            Equal => Equal,
            NotEqual => NotEqual,
            OrderedNotEqual => OrderedNotEqual,
            UnorderedOrEqual => UnorderedOrEqual,
            LessThan => GreaterThan,
            LessThanOrEqual => GreaterThanOrEqual,
            GreaterThan => LessThan,
            GreaterThanOrEqual => LessThanOrEqual,
            UnorderedOrLessThan => UnorderedOrGreaterThan,
            UnorderedOrLessThanOrEqual => UnorderedOrGreaterThanOrEqual,
            UnorderedOrGreaterThan => UnorderedOrLessThan,
            UnorderedOrGreaterThanOrEqual => UnorderedOrLessThanOrEqual,
        }
    }
}

impl Display for FloatCC {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        use self::FloatCC::*;
        f.write_str(match *self {
            Ordered => "ord",
            Unordered => "uno",
            Equal => "eq",
            NotEqual => "ne",
            OrderedNotEqual => "one",
            UnorderedOrEqual => "ueq",
            LessThan => "lt",
            LessThanOrEqual => "le",
            GreaterThan => "gt",
            GreaterThanOrEqual => "ge",
            UnorderedOrLessThan => "ult",
            UnorderedOrLessThanOrEqual => "ule",
            UnorderedOrGreaterThan => "ugt",
            UnorderedOrGreaterThanOrEqual => "uge",
        })
    }
}

impl FromStr for FloatCC {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::FloatCC::*;
        match s {
            "ord" => Ok(Ordered),
            "uno" => Ok(Unordered),
            "eq" => Ok(Equal),
            "ne" => Ok(NotEqual),
            "one" => Ok(OrderedNotEqual),
            "ueq" => Ok(UnorderedOrEqual),
            "lt" => Ok(LessThan),
            "le" => Ok(LessThanOrEqual),
            "gt" => Ok(GreaterThan),
            "ge" => Ok(GreaterThanOrEqual),
            "ult" => Ok(UnorderedOrLessThan),
            "ule" => Ok(UnorderedOrLessThanOrEqual),
            "ugt" => Ok(UnorderedOrGreaterThan),
            "uge" => Ok(UnorderedOrGreaterThanOrEqual),
            _ => Err(()),
        }
    }
}

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

    static INT_ALL: [IntCC; 10] = [
        IntCC::Equal,
        IntCC::NotEqual,
        IntCC::SignedLessThan,
        IntCC::SignedGreaterThanOrEqual,
        IntCC::SignedGreaterThan,
        IntCC::SignedLessThanOrEqual,
        IntCC::UnsignedLessThan,
        IntCC::UnsignedGreaterThanOrEqual,
        IntCC::UnsignedGreaterThan,
        IntCC::UnsignedLessThanOrEqual,
    ];

    #[test]
    fn int_inverse() {
        for r in &INT_ALL {
            let cc = *r;
            let inv = cc.inverse();
            assert!(cc != inv);
            assert_eq!(inv.inverse(), cc);
        }
    }

    #[test]
    fn int_reverse() {
        for r in &INT_ALL {
            let cc = *r;
            let rev = cc.reverse();
            assert_eq!(rev.reverse(), cc);
        }
    }

    #[test]
    fn int_display() {
        for r in &INT_ALL {
            let cc = *r;
            assert_eq!(cc.to_string().parse(), Ok(cc));
        }
        assert_eq!("bogus".parse::<IntCC>(), Err(()));
    }

    static FLOAT_ALL: [FloatCC; 14] = [
        FloatCC::Ordered,
        FloatCC::Unordered,
        FloatCC::Equal,
        FloatCC::NotEqual,
        FloatCC::OrderedNotEqual,
        FloatCC::UnorderedOrEqual,
        FloatCC::LessThan,
        FloatCC::LessThanOrEqual,
        FloatCC::GreaterThan,
        FloatCC::GreaterThanOrEqual,
        FloatCC::UnorderedOrLessThan,
        FloatCC::UnorderedOrLessThanOrEqual,
        FloatCC::UnorderedOrGreaterThan,
        FloatCC::UnorderedOrGreaterThanOrEqual,
    ];

    #[test]
    fn float_inverse() {
        for r in &FLOAT_ALL {
            let cc = *r;
            let inv = cc.inverse();
            assert!(cc != inv);
            assert_eq!(inv.inverse(), cc);
        }
    }

    #[test]
    fn float_reverse() {
        for r in &FLOAT_ALL {
            let cc = *r;
            let rev = cc.reverse();
            assert_eq!(rev.reverse(), cc);
        }
    }

    #[test]
    fn float_display() {
        for r in &FLOAT_ALL {
            let cc = *r;
            assert_eq!(cc.to_string().parse(), Ok(cc));
        }
        assert_eq!("bogus".parse::<FloatCC>(), Err(()));
    }
}
<|endoftext|>


<commit_before>use core::clone::Clone;
use core::iter::Iterator;
use core::mem::size_of;
use core::ops::Drop;
use core::option::Option;
use core::ptr;
use core::slice;
use core::slice::SliceExt;

use syscall::call::*;

#[macro_export]
macro_rules! vec {
    ($($x:expr),*) => (
        Vec::from_slice(&[$($x),*])
    );
    ($($x:expr,)*) => (vec![$($x),*])
}

/// An iterator over a vec
pub struct VecIterator<'a, T: 'a> {
    vec: &'a Vec<T>,
    offset: usize,
}

impl <'a, T> Iterator for VecIterator<'a, T> {
    type Item = &'a mut T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.vec.get(self.offset) {
            Option::Some(item) => {
                self.offset += 1;
                Option::Some(item)
            }
            Option::None => {
                Option::None
            }
        }
    }
}

/// An owned iterator over a vec
pub struct OwnedVecIterator<T>
    where T: Clone
{
    vec: Vec<T>,
    offset: usize,
}

impl<'a, T> Iterator for OwnedVecIterator<T>
            where T: Clone {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.vec.get(self.offset) {
            Option::Some(item) => {
                self.offset += 1;
                Option::Some(item.clone())
            }
            Option::None => {
                Option::None
            }
        }
    }
}
/// A owned, heap allocated list of elements
pub struct Vec<T> {
    pub data: *mut T,
    pub length: usize,
}

impl <T> Vec<T> {
    /// Create a empty vector
    pub fn new() -> Self {
        Vec::<T> {
            data: 0 as *mut T,
            length: 0,
        }
    }

    /// Convert to pointer
    pub unsafe fn as_ptr(&self) -> *const T {
        self.data
    }

    /// Convert from a raw (unsafe) buffer
    pub unsafe fn from_raw_buf(ptr: *const T, len: usize) -> Self {
        let data = sys_alloc(size_of::<T>() * len) as *mut T;

        ptr::copy(ptr, data, len);

        Vec::<T> {
            data: data,
            length: len,
        }
    }

    pub fn from_slice(slice: &[T]) -> Self {
        let data;
        unsafe {
            data = sys_alloc(size_of::<T>() * slice.len()) as *mut T;

            ptr::copy(slice.as_ptr(), data, slice.len());
        }

        Vec::<T> {
            data: data,
            length: slice.len(),
        }
    }

    /// Get the nth element. Returns None if out of bounds.
    pub fn get(&self, i: usize) -> Option<&mut T> {
        if i >= self.length {
            Option::None
        } else {
            unsafe { Option::Some(&mut *self.data.offset(i as isize)) }
        }
    }

    /// Set the nth element
    pub fn set(&self, i: usize, value: T) {
        if i <= self.length {
            unsafe {
                ptr::write(self.data.offset(i as isize), value);
            }
        }
    }

    /// Insert element at a given position
    pub fn insert(&mut self, i: usize, value: T) {
        if i <= self.length {
            self.length += 1;
            unsafe {
                self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                //Move all things ahead of insert forward one
                let mut j = self.length - 1;
                while j > i {
                    ptr::write(self.data.offset(j as isize),
                               ptr::read(self.data.offset(j as isize - 1)));
                    j -= 1;
                }

                ptr::write(self.data.offset(i as isize), value);
            }
        }
    }

    /// Remove a element and return it as a Option
    pub fn remove(&mut self, i: usize) -> Option<T> {
        if i < self.length {
            self.length -= 1;
            unsafe {
                let item = ptr::read(self.data.offset(i as isize));

                //Move all things ahead of remove back one
                let mut j = i;
                while j < self.length {
                    ptr::write(self.data.offset(j as isize),
                               ptr::read(self.data.offset(j as isize + 1)));
                    j += 1;
                }

                self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                Option::Some(item)
            }
        } else {
            Option::None
        }
    }

    /// Push an element to a vector
    pub fn push(&mut self, value: T) {
        self.length += 1;
        unsafe {
            self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;
            ptr::write(self.data.offset(self.length as isize - 1), value);
        }
    }

    /// Pop the last element
    pub fn pop(&mut self) -> Option<T> {
        if self.length > 0 {
            self.length -= 1;
            unsafe {
                let item = ptr::read(self.data.offset(self.length as isize));
                self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                Option::Some(item)
            }
        } else {
            Option::None
        }
    }

    /// Get the length of the vector
    pub fn len(&self) -> usize {
        self.length
    }

    /// Create an iterator
    pub fn iter(&self) -> VecIterator<T> {
        VecIterator {
            vec: self,
            offset: 0,
        }
    }

    /// Convert into an iterator
    pub fn into_iter(self) -> OwnedVecIterator<T>
        where T: Clone
    {
        OwnedVecIterator {
            vec: self,
            offset: 0,
        }
    }

    // TODO: Consider returning a slice instead
    pub fn sub(&self, start: usize, count: usize) -> Self {
        let mut i = start;
        if i > self.len() {
            i = self.len();
        }

        let mut j = i + count;
        if j > self.len() {
            j = self.len();
        }

        let length = j - i;
        if length == 0 {
            return Vec::new();
        }

        unsafe {
            let data = sys_alloc(length * size_of::<T>()) as *mut T;

            for k in i..j {
                ptr::write(data.offset((k - i) as isize),
                           ptr::read(self.data.offset(k as isize)));
            }

            Vec {
                data: data,
                length: length,
            }
        }
    }

    pub fn as_slice(&self) -> &[T] {
        if self.data as usize > 0 && self.length > 0 {
            unsafe { slice::from_raw_parts(self.data, self.length) }
        } else {
            &[]
        }
    }
}

impl<T> Vec<T> where T: Clone {
    /// Append a vector to another vector
    pub fn push_all(&mut self, vec: &Self) {
        let mut i = self.length as isize;
        self.length += vec.len();
        unsafe {
            self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

            for value in vec.iter() {
                ptr::write(self.data.offset(i), value.clone());
                i += 1;
            }
        }
    }
}

impl<T> Clone for Vec<T> where T: Clone {
    fn clone(&self) -> Self {
        let mut ret = Vec::new();
        ret.push_all(self);
        ret
    }
}

impl<T> Drop for Vec<T> {
    fn drop(&mut self) {
        unsafe {
            for i in 0..self.len() {
                ptr::read(self.data.offset(i as isize));
            }

            sys_unalloc(self.data as usize);
            self.data = 0 as *mut T;
            self.length = 0;
        }
    }
}
<commit_msg>Vec uses kernel allocators<commit_after>use core::clone::Clone;
use core::iter::Iterator;
use core::mem::size_of;
use core::ops::Drop;
use core::option::Option;
use core::ptr;
use core::slice;
use core::slice::SliceExt;

use common::memory::*;

#[macro_export]
macro_rules! vec {
    ($($x:expr),*) => (
        Vec::from_slice(&[$($x),*])
    );
    ($($x:expr,)*) => (vec![$($x),*])
}

/// An iterator over a vec
pub struct VecIterator<'a, T: 'a> {
    vec: &'a Vec<T>,
    offset: usize,
}

impl <'a, T> Iterator for VecIterator<'a, T> {
    type Item = &'a mut T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.vec.get(self.offset) {
            Option::Some(item) => {
                self.offset += 1;
                Option::Some(item)
            }
            Option::None => {
                Option::None
            }
        }
    }
}

/// An owned iterator over a vec
pub struct OwnedVecIterator<T>
    where T: Clone
{
    vec: Vec<T>,
    offset: usize,
}

impl<'a, T> Iterator for OwnedVecIterator<T>
            where T: Clone {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.vec.get(self.offset) {
            Option::Some(item) => {
                self.offset += 1;
                Option::Some(item.clone())
            }
            Option::None => {
                Option::None
            }
        }
    }
}
/// A owned, heap allocated list of elements
pub struct Vec<T> {
    pub data: *mut T,
    pub length: usize,
}

impl <T> Vec<T> {
    /// Create a empty vector
    pub fn new() -> Self {
        Vec::<T> {
            data: 0 as *mut T,
            length: 0,
        }
    }

    /// Convert to pointer
    pub unsafe fn as_ptr(&self) -> *const T {
        self.data
    }

    /// Convert from a raw (unsafe) buffer
    pub unsafe fn from_raw_buf(ptr: *const T, len: usize) -> Self {
        let data = alloc(size_of::<T>() * len) as *mut T;

        ptr::copy(ptr, data, len);

        Vec::<T> {
            data: data,
            length: len,
        }
    }

    pub fn from_slice(slice: &[T]) -> Self {
        let data;
        unsafe {
            data = alloc(size_of::<T>() * slice.len()) as *mut T;

            ptr::copy(slice.as_ptr(), data, slice.len());
        }

        Vec::<T> {
            data: data,
            length: slice.len(),
        }
    }

    /// Get the nth element. Returns None if out of bounds.
    pub fn get(&self, i: usize) -> Option<&mut T> {
        if i >= self.length {
            Option::None
        } else {
            unsafe { Option::Some(&mut *self.data.offset(i as isize)) }
        }
    }

    /// Set the nth element
    pub fn set(&self, i: usize, value: T) {
        if i <= self.length {
            unsafe {
                ptr::write(self.data.offset(i as isize), value);
            }
        }
    }

    /// Insert element at a given position
    pub fn insert(&mut self, i: usize, value: T) {
        if i <= self.length {
            self.length += 1;
            unsafe {
                self.data = realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                //Move all things ahead of insert forward one
                let mut j = self.length - 1;
                while j > i {
                    ptr::write(self.data.offset(j as isize),
                               ptr::read(self.data.offset(j as isize - 1)));
                    j -= 1;
                }

                ptr::write(self.data.offset(i as isize), value);
            }
        }
    }

    /// Remove a element and return it as a Option
    pub fn remove(&mut self, i: usize) -> Option<T> {
        if i < self.length {
            self.length -= 1;
            unsafe {
                let item = ptr::read(self.data.offset(i as isize));

                //Move all things ahead of remove back one
                let mut j = i;
                while j < self.length {
                    ptr::write(self.data.offset(j as isize),
                               ptr::read(self.data.offset(j as isize + 1)));
                    j += 1;
                }

                self.data = realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                Option::Some(item)
            }
        } else {
            Option::None
        }
    }

    /// Push an element to a vector
    pub fn push(&mut self, value: T) {
        self.length += 1;
        unsafe {
            self.data = realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;
            ptr::write(self.data.offset(self.length as isize - 1), value);
        }
    }

    /// Pop the last element
    pub fn pop(&mut self) -> Option<T> {
        if self.length > 0 {
            self.length -= 1;
            unsafe {
                let item = ptr::read(self.data.offset(self.length as isize));
                self.data = realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                Option::Some(item)
            }
        } else {
            Option::None
        }
    }

    /// Get the length of the vector
    pub fn len(&self) -> usize {
        self.length
    }

    /// Create an iterator
    pub fn iter(&self) -> VecIterator<T> {
        VecIterator {
            vec: self,
            offset: 0,
        }
    }

    /// Convert into an iterator
    pub fn into_iter(self) -> OwnedVecIterator<T>
        where T: Clone
    {
        OwnedVecIterator {
            vec: self,
            offset: 0,
        }
    }

    // TODO: Consider returning a slice instead
    pub fn sub(&self, start: usize, count: usize) -> Self {
        let mut i = start;
        if i > self.len() {
            i = self.len();
        }

        let mut j = i + count;
        if j > self.len() {
            j = self.len();
        }

        let length = j - i;
        if length == 0 {
            return Vec::new();
        }

        unsafe {
            let data = alloc(length * size_of::<T>()) as *mut T;

            for k in i..j {
                ptr::write(data.offset((k - i) as isize),
                           ptr::read(self.data.offset(k as isize)));
            }

            Vec {
                data: data,
                length: length,
            }
        }
    }

    pub fn as_slice(&self) -> &[T] {
        if self.data as usize > 0 && self.length > 0 {
            unsafe { slice::from_raw_parts(self.data, self.length) }
        } else {
            &[]
        }
    }
}

impl<T> Vec<T> where T: Clone {
    /// Append a vector to another vector
    pub fn push_all(&mut self, vec: &Self) {
        let mut i = self.length as isize;
        self.length += vec.len();
        unsafe {
            self.data = realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

            for value in vec.iter() {
                ptr::write(self.data.offset(i), value.clone());
                i += 1;
            }
        }
    }
}

impl<T> Clone for Vec<T> where T: Clone {
    fn clone(&self) -> Self {
        let mut ret = Vec::new();
        ret.push_all(self);
        ret
    }
}

impl<T> Drop for Vec<T> {
    fn drop(&mut self) {
        unsafe {
            for i in 0..self.len() {
                ptr::read(self.data.offset(i as isize));
            }

            unalloc(self.data as usize);
            self.data = 0 as *mut T;
            self.length = 0;
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 The Exonum Team
//
// 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.

use std::collections::HashSet;

use criterion::{
    black_box, AxisScale, Bencher, Criterion, ParameterizedBenchmark, PlotConfiguration, Throughput,
};
use rand::{Rng, RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;

use exonum_crypto::Hash;
use exonum_merkledb::{
    proof_map_index::PROOF_MAP_KEY_SIZE as KEY_SIZE, Database, MapIndex, ProofListIndex,
    ProofMapIndex, TemporaryDB,
};

const NAME: &str = "name";
const FAMILY: &str = "index_family";
const SAMPLE_SIZE: usize = 10;
const CHUNK_SIZE: usize = 64;
const SEED: [u8; 16] = [100; 16];

#[cfg(not(feature = "long_benchmarks"))]
const ITEM_COUNTS: [usize; 2] = [1_000, 10_000];

#[cfg(all(test, feature = "long_benchmarks"))]
const ITEM_COUNTS: [usize; 4] = [1_000, 10_000, 100_000, 1_000_000];

fn generate_random_kv(len: usize) -> Vec<(Hash, Vec<u8>)> {
    let mut rng = XorShiftRng::from_seed(SEED);
    let mut exists_keys = HashSet::new();
    let mut base = [0; KEY_SIZE];
    rng.fill_bytes(&mut base);
    let base = base;

    let kv_generator = |_| {
        let mut v = vec![0; CHUNK_SIZE];
        // Generate only unique keys.
        let mut k = base;
        let byte: usize = rng.gen_range(0, 31);
        k[byte] = rng.gen::<u8>();

        rng.fill_bytes(&mut v);
        while exists_keys.contains(&k) {
            rng.fill_bytes(&mut k);
        }
        exists_keys.insert(k);
        (Hash::new(k), v)
    };

    (0..len).map(kv_generator).collect::<Vec<_>>()
}

fn plain_map_index_insert(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let fork = db.fork();
            {
                let mut table = MapIndex::new(NAME, &fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn plain_map_index_with_family_insert(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let fork = db.fork();
            {
                let mut table = MapIndex::new_in_family(NAME, FAMILY, &fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn plain_map_index_iter(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let fork = db.fork();

    {
        let mut table = MapIndex::new(NAME, &fork);
        assert!(table.keys().next().is_none());
        for item in data {
            table.put(&item.0, item.1);
        }
    }
    db.merge_sync(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Hash, Vec<u8>> = MapIndex::new(NAME, &snapshot);
            for (key, value) in &index {
                black_box(key);
                black_box(value);
            }
        },
    );
}

fn plain_map_index_with_family_iter(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let fork = db.fork();

    {
        let mut table = MapIndex::new_in_family(NAME, FAMILY, &fork);
        assert!(table.keys().next().is_none());
        for item in data {
            table.put(&item.0, item.1);
        }
    }
    db.merge(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Hash, Vec<u8>> =
                MapIndex::new_in_family(NAME, FAMILY, &snapshot);
            for (key, value) in &index {
                black_box(key);
                black_box(value);
            }
        },
    );
}

fn plain_map_index_read(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let fork = db.fork();

    {
        let mut table = MapIndex::new(NAME, &fork);
        assert!(table.keys().next().is_none());
        for item in data.clone() {
            table.put(&item.0, item.1);
        }
    }
    db.merge_sync(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Hash, Vec<u8>> = MapIndex::new(NAME, &snapshot);
            for item in &data {
                let value = index.get(&item.0);
                black_box(value);
            }
        },
    );
}

fn plain_map_index_with_family_read(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let fork = db.fork();

    {
        let mut table = MapIndex::new_in_family(NAME, FAMILY, &fork);
        assert!(table.keys().next().is_none());
        for item in data.clone() {
            table.put(&item.0, item.1);
        }
    }
    db.merge_sync(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Hash, Vec<u8>> =
                MapIndex::new_in_family(NAME, FAMILY, &snapshot);
            for item in &data {
                let value = index.get(&item.0);
                black_box(value);
            }
        },
    );
}

fn proof_list_append(b: &mut Bencher, len: usize) {
    let mut rng = XorShiftRng::from_seed(SEED);
    let data = (0..len)
        .map(|_| {
            let mut chunk = vec![0; CHUNK_SIZE];
            rng.fill_bytes(&mut chunk);
            chunk
        })
        .collect::<Vec<_>>();

    let db = TemporaryDB::default();
    b.iter_with_setup(
        || (db.fork(), data.clone()),
        |(storage, data)| {
            let mut table = ProofListIndex::new(NAME, &storage);
            assert!(table.is_empty());
            for item in data {
                table.push(item);
            }
        },
    );
}

fn proof_map_insert_without_merge(b: &mut Bencher, len: usize) {
    let db = TemporaryDB::default();
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (db.fork(), data.clone()),
        |(storage, data)| {
            let mut table = ProofMapIndex::new(NAME, &storage);
            for item in data {
                table.put(&item.0, item.1);
            }
        },
    );
}

fn proof_map_insert_with_merge(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let mut fork = db.fork();
            {
                let mut table = ProofMapIndex::new(NAME, &fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn proof_map_index_build_proofs(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let storage = db.fork();
    let mut table = ProofMapIndex::new(NAME, &storage);

    for item in &data {
        table.put(&item.0, item.1.clone());
    }
    let table_merkle_root = table.merkle_root();
    let mut proofs = Vec::with_capacity(data.len());

    b.iter(|| {
        proofs.clear();
        proofs.extend(data.iter().map(|item| table.get_proof(item.0)));
    });

    for (i, proof) in proofs.into_iter().enumerate() {
        let checked_proof = proof.check().unwrap();
        assert_eq!(*checked_proof.entries().next().unwrap().1, data[i].1);
        assert_eq!(checked_proof.merkle_root(), table_merkle_root);
    }
}

fn proof_map_index_verify_proofs(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let storage = db.fork();
    let mut table = ProofMapIndex::new(NAME, &storage);

    for item in &data {
        table.put(&item.0, item.1.clone());
    }
    let table_merkle_root = table.merkle_root();
    let proofs: Vec<_> = data.iter().map(|item| table.get_proof(item.0)).collect();

    b.iter(|| {
        for (i, proof) in proofs.iter().enumerate() {
            let checked_proof = proof.clone().check().unwrap();
            assert_eq!(*checked_proof.entries().next().unwrap().1, data[i].1);
            assert_eq!(checked_proof.merkle_root(), table_merkle_root);
        }
    });
}

fn bench_fn<F>(c: &mut Criterion, name: &str, benchmark: F)
where
    F: Fn(&mut Bencher, usize) + 'static,
{
    let item_counts = ITEM_COUNTS.iter().cloned();
    c.bench(
        name,
        ParameterizedBenchmark::new(
            "items",
            move |b: &mut Bencher, &len: &usize| benchmark(b, len),
            item_counts,
        )
        .throughput(|s| Throughput::Elements(*s as u32))
        .plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic))
        .sample_size(SAMPLE_SIZE),
    );
}

pub fn bench_storage(c: &mut Criterion) {
    exonum_crypto::init();
    // MapIndex
    bench_fn(c, "plain_map/insert", plain_map_index_insert);
    bench_fn(c, "plain_map/iter", plain_map_index_iter);
    bench_fn(
        c,
        "plain_map_with_family/insert",
        plain_map_index_with_family_insert,
    );
    bench_fn(
        c,
        "plain_map_with_family/iter",
        plain_map_index_with_family_iter,
    );
    bench_fn(c, "plain_map/read", plain_map_index_read);
    bench_fn(
        c,
        "plain_map_with_family/read",
        plain_map_index_with_family_read,
    );
    // ProofListIndex
    bench_fn(c, "proof_list/append", proof_list_append);
    // ProofMapIndex
    bench_fn(
        c,
        "proof_map/insert/no_merge",
        proof_map_insert_without_merge,
    );
    bench_fn(c, "proof_map/insert/merge", proof_map_insert_with_merge);
    bench_fn(c, "proof_map/proofs/build", proof_map_index_build_proofs);
    bench_fn(
        c,
        "proof_map/proofs/validate",
        proof_map_index_verify_proofs,
    );
}
<commit_msg>Extend default item count.<commit_after>// Copyright 2019 The Exonum Team
//
// 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.

use std::collections::HashSet;

use criterion::{
    black_box, AxisScale, Bencher, Criterion, ParameterizedBenchmark, PlotConfiguration, Throughput,
};
use rand::{Rng, RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;

use exonum_crypto::Hash;
use exonum_merkledb::{
    proof_map_index::PROOF_MAP_KEY_SIZE as KEY_SIZE, Database, MapIndex, ProofListIndex,
    ProofMapIndex, TemporaryDB,
};

const NAME: &str = "name";
const FAMILY: &str = "index_family";
const SAMPLE_SIZE: usize = 10;
const CHUNK_SIZE: usize = 64;
const SEED: [u8; 16] = [100; 16];

#[cfg(not(feature = "long_benchmarks"))]
const ITEM_COUNTS: [usize; 3] = [1_000, 10_000, 100_000];

#[cfg(all(test, feature = "long_benchmarks"))]
const ITEM_COUNTS: [usize; 4] = [1_000, 10_000, 100_000, 1_000_000];

fn generate_random_kv(len: usize) -> Vec<(Hash, Vec<u8>)> {
    let mut rng = XorShiftRng::from_seed(SEED);
    let mut exists_keys = HashSet::new();
    let mut base = [0; KEY_SIZE];
    rng.fill_bytes(&mut base);
    let base = base;

    let kv_generator = |_| {
        let mut v = vec![0; CHUNK_SIZE];
        // Generate only unique keys.
        let mut k = base;
        let byte: usize = rng.gen_range(0, 31);
        k[byte] = rng.gen::<u8>();

        rng.fill_bytes(&mut v);
        while exists_keys.contains(&k) {
            rng.fill_bytes(&mut k);
        }
        exists_keys.insert(k);
        (Hash::new(k), v)
    };

    (0..len).map(kv_generator).collect::<Vec<_>>()
}

fn plain_map_index_insert(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let fork = db.fork();
            {
                let mut table = MapIndex::new(NAME, &fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn plain_map_index_with_family_insert(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let fork = db.fork();
            {
                let mut table = MapIndex::new_in_family(NAME, FAMILY, &fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn plain_map_index_iter(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let fork = db.fork();

    {
        let mut table = MapIndex::new(NAME, &fork);
        assert!(table.keys().next().is_none());
        for item in data {
            table.put(&item.0, item.1);
        }
    }
    db.merge_sync(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Hash, Vec<u8>> = MapIndex::new(NAME, &snapshot);
            for (key, value) in &index {
                black_box(key);
                black_box(value);
            }
        },
    );
}

fn plain_map_index_with_family_iter(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let fork = db.fork();

    {
        let mut table = MapIndex::new_in_family(NAME, FAMILY, &fork);
        assert!(table.keys().next().is_none());
        for item in data {
            table.put(&item.0, item.1);
        }
    }
    db.merge(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Hash, Vec<u8>> =
                MapIndex::new_in_family(NAME, FAMILY, &snapshot);
            for (key, value) in &index {
                black_box(key);
                black_box(value);
            }
        },
    );
}

fn plain_map_index_read(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let fork = db.fork();

    {
        let mut table = MapIndex::new(NAME, &fork);
        assert!(table.keys().next().is_none());
        for item in data.clone() {
            table.put(&item.0, item.1);
        }
    }
    db.merge_sync(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Hash, Vec<u8>> = MapIndex::new(NAME, &snapshot);
            for item in &data {
                let value = index.get(&item.0);
                black_box(value);
            }
        },
    );
}

fn plain_map_index_with_family_read(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let fork = db.fork();

    {
        let mut table = MapIndex::new_in_family(NAME, FAMILY, &fork);
        assert!(table.keys().next().is_none());
        for item in data.clone() {
            table.put(&item.0, item.1);
        }
    }
    db.merge_sync(fork.into_patch()).unwrap();

    b.iter_with_setup(
        || db.snapshot(),
        |snapshot| {
            let index: MapIndex<_, Hash, Vec<u8>> =
                MapIndex::new_in_family(NAME, FAMILY, &snapshot);
            for item in &data {
                let value = index.get(&item.0);
                black_box(value);
            }
        },
    );
}

fn proof_list_append(b: &mut Bencher, len: usize) {
    let mut rng = XorShiftRng::from_seed(SEED);
    let data = (0..len)
        .map(|_| {
            let mut chunk = vec![0; CHUNK_SIZE];
            rng.fill_bytes(&mut chunk);
            chunk
        })
        .collect::<Vec<_>>();

    let db = TemporaryDB::default();
    b.iter_with_setup(
        || (db.fork(), data.clone()),
        |(storage, data)| {
            let mut table = ProofListIndex::new(NAME, &storage);
            assert!(table.is_empty());
            for item in data {
                table.push(item);
            }
        },
    );
}

fn proof_map_insert_without_merge(b: &mut Bencher, len: usize) {
    let db = TemporaryDB::default();
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (db.fork(), data.clone()),
        |(storage, data)| {
            let mut table = ProofMapIndex::new(NAME, &storage);
            for item in data {
                table.put(&item.0, item.1);
            }
        },
    );
}

fn proof_map_insert_with_merge(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    b.iter_with_setup(
        || (TemporaryDB::default(), data.clone()),
        |(db, data)| {
            let mut fork = db.fork();
            {
                let mut table = ProofMapIndex::new(NAME, &fork);
                for item in data {
                    table.put(&item.0, item.1);
                }
            }
            db.merge_sync(fork.into_patch()).unwrap();
        },
    );
}

fn proof_map_index_build_proofs(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let storage = db.fork();
    let mut table = ProofMapIndex::new(NAME, &storage);

    for item in &data {
        table.put(&item.0, item.1.clone());
    }
    let table_merkle_root = table.merkle_root();
    let mut proofs = Vec::with_capacity(data.len());

    b.iter(|| {
        proofs.clear();
        proofs.extend(data.iter().map(|item| table.get_proof(item.0)));
    });

    for (i, proof) in proofs.into_iter().enumerate() {
        let checked_proof = proof.check().unwrap();
        assert_eq!(*checked_proof.entries().next().unwrap().1, data[i].1);
        assert_eq!(checked_proof.merkle_root(), table_merkle_root);
    }
}

fn proof_map_index_verify_proofs(b: &mut Bencher, len: usize) {
    let data = generate_random_kv(len);
    let db = TemporaryDB::default();
    let storage = db.fork();
    let mut table = ProofMapIndex::new(NAME, &storage);

    for item in &data {
        table.put(&item.0, item.1.clone());
    }
    let table_merkle_root = table.merkle_root();
    let proofs: Vec<_> = data.iter().map(|item| table.get_proof(item.0)).collect();

    b.iter(|| {
        for (i, proof) in proofs.iter().enumerate() {
            let checked_proof = proof.clone().check().unwrap();
            assert_eq!(*checked_proof.entries().next().unwrap().1, data[i].1);
            assert_eq!(checked_proof.merkle_root(), table_merkle_root);
        }
    });
}

fn bench_fn<F>(c: &mut Criterion, name: &str, benchmark: F)
where
    F: Fn(&mut Bencher, usize) + 'static,
{
    let item_counts = ITEM_COUNTS.iter().cloned();
    c.bench(
        name,
        ParameterizedBenchmark::new(
            "items",
            move |b: &mut Bencher, &len: &usize| benchmark(b, len),
            item_counts,
        )
        .throughput(|s| Throughput::Elements(*s as u32))
        .plot_config(PlotConfiguration::default().summary_scale(AxisScale::Logarithmic))
        .sample_size(SAMPLE_SIZE),
    );
}

pub fn bench_storage(c: &mut Criterion) {
    exonum_crypto::init();
    // MapIndex
    bench_fn(c, "storage/plain_map/insert", plain_map_index_insert);
    bench_fn(c, "storage/plain_map/iter", plain_map_index_iter);
    bench_fn(
        c,
        "storage/plain_map_with_family/insert",
        plain_map_index_with_family_insert,
    );
    bench_fn(
        c,
        "storage/plain_map_with_family/iter",
        plain_map_index_with_family_iter,
    );
    bench_fn(c, "storage/plain_map/read", plain_map_index_read);
    bench_fn(
        c,
        "storage/plain_map_with_family/read",
        plain_map_index_with_family_read,
    );
    // ProofListIndex
    bench_fn(c, "storage/proof_list/append", proof_list_append);
    // ProofMapIndex
    bench_fn(
        c,
        "storage/proof_map/insert/no_merge",
        proof_map_insert_without_merge,
    );
    bench_fn(c, "storage/proof_map/insert/merge", proof_map_insert_with_merge);
    bench_fn(c, "storage/proof_map/proofs/build", proof_map_index_build_proofs);
    bench_fn(
        c,
        "storage/proof_map/proofs/validate",
        proof_map_index_verify_proofs,
    );
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::fmt;

use self::err::ConfigError;
/* includes the generated code from PEG
	- ${PROJECT_DIR}/targetbuild/ruwm-*out/
*/
// TODO
// This code need some major cleanup

use config_parser::*;

#[allow(dead_code)]
pub mod config_grammar {
	include!(concat!(env!("OUT_DIR"), "/config_grammar.rs"));
}

#[derive(Debug)]
pub struct Parser {
  variables: Variables,
  bindSym:  Vec<Config>,
  exec: Vec<Config>,
  floatingMod: Vec<Config>,
}

impl fmt::Display for Parser {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Variables: {:?}, \
      bindSym: {:?}, \
      exec: {:?}, \
      floatingMod: {:?}",
      self.variables, self.bindSym, self.exec, self.floatingMod
    )
  }
}

  /*
   * Parse configuration file, interning Variables,
   * and setting up the Parser Struct
   * and moving ownership of the parsed file from
   * the rust-peg library to my own structures
   */
fn parse(mut file: &mut File)
  -> Result<(Variables, Vec<Config>, Vec<Config>, Vec<Config>), ConfigError>
{
  let mut config = String::new();
  // println!("first config, empty: {}", config);
  file.read_to_string(&mut config)?;
  println!("Should not be empty: {}", config);
  let config = config_grammar::content(config.as_ref())?;

  // create data structs for each configuration type
  let mut vars = Variables::new();
  let mut bindSym = Vec::new();
  let mut exec = Vec::new();
  let mut floatingMod = Vec::new();

  for x in config.iter() {
    match *x {
      Config::Set(ref v, ref s) => vars.set(v.to_owned(), s.to_owned())?,
      Config::Exec(ref a) => exec.push(Config::Exec(a.to_owned()).clone()),
      Config::BindSym(ref s, ref a) => bindSym.push(Config::BindSym(s.to_owned(),a.to_owned()).clone()),
      Config::FloatingMod(ref s) => floatingMod.push(Config::FloatingMod(s.to_owned()).clone()),
      _ => {},
    };
  }
  Ok((vars, bindSym, exec, floatingMod))
}

impl Parser {

  pub fn new(config_file: &str) -> Result<Parser, ConfigError>  {
    let mut f = File::open(config_file)?;

    let result = parse(&mut f)?;
    Ok(Parser {
      variables: result.0,
      bindSym: result.1,
      exec: result.2,
      floatingMod: result.3,
    })
  }
}

#[cfg(test)]
#[test]
fn test_grammar() {
  // TODO
  // the fact that the first line must be at the very beginning
  // is not a very big deal, since we can just strip all whitespace until
  // we reach an ascii character
  // but if there is a easy way to fix this in the grammar, then that is the
  // better option
	match config_grammar::content("# A comment
		bindsym LeftGui+1 workspace 1

bindsym $super+$rand exec 'termite --config $HOME/.config/termite/config'
   bindsym RightGui+2 workspace 2 # this is another comment

set $super Ctrl+Mod4
  # This is a comment
set $ws0 '0:emp'
bindsym RightGui+3 workspace $ws0
bindsym Hello+3 workspace 5

bindsym $super+$left focus left
bindsym $super+Ctrl focus down
bindsym $super+$up focus up
bindsym $super+$right focus right
bindsym $super+1 workspace $ws0

exec --no-startup-id 'nm-applet'

set $left h

"){
		Ok(r) => println!("Parsed as: {:?}", r),
		Err(e) => println!("Parse error: {}", e),
	};

}

#[cfg(test)]
#[test]
fn test_parser() {
  let parser = match Parser::new("/home/insi/Projects/ruwm/src/config_parser/config.test") {
    Ok(s) => s,
    Err(e) => panic!("{}", e)
  };

  println!("Parser: {}", parser.to_string());
}
<commit_msg>got rid of unecessary parse method<commit_after>use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::fmt;

use self::err::ConfigError;
/* includes the generated code from PEG
	- ${PROJECT_DIR}/targetbuild/ruwm-*out/
*/
// TODO
// This code need some major cleanup

use config_parser::*;

#[allow(dead_code)]
pub mod config_grammar {
	include!(concat!(env!("OUT_DIR"), "/config_grammar.rs"));
}

#[derive(Debug)]
pub struct Parser {
  variables: Variables,
  bindSym:  Vec<Config>,
  exec: Vec<Config>,
  floatingMod: Vec<Config>,
}

impl fmt::Display for Parser {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "Variables: {:?}, \
      bindSym: {:?}, \
      exec: {:?}, \
      floatingMod: {:?}",
      self.variables, self.bindSym, self.exec, self.floatingMod
    )
  }
}

  /*
   * Set up the Parser Struct
   * move ownership of the parsed file from
   * the rust-peg grammar (rust-peg) to my own structures
   */
impl Parser {

  pub fn new(config_file: &str) -> Result<Parser, ConfigError>  {
    let mut f = File::open(config_file)?;

    let mut config = String::new();
    f.read_to_string(&mut config)?;
    let config = config_grammar::content(config.as_ref())?;

    // create data structs for each configuration type
    let mut variables = Variables::new();
    let mut bindSym = Vec::new();
    let mut exec = Vec::new();
    let mut floatingMod = Vec::new();

    for x in config.iter() {
      match *x {
        Config::Set(ref v, ref s) => variables.set(v.to_owned(), s.to_owned())?,
        Config::Exec(ref a) => exec.push(Config::Exec(a.to_owned())),
        Config::BindSym(ref s, ref a) => bindSym.push(Config::BindSym(s.to_owned(),a.to_owned())),
        Config::FloatingMod(ref s) => floatingMod.push(Config::FloatingMod(s.to_owned())),
        _ => {},
      };
    }

    // let result = parse(&mut f)?;
    Ok(Parser {
      variables,
      bindSym,
      exec,
      floatingMod,
    })
  }
}

#[cfg(test)]
#[test]
fn test_grammar() {
  // TODO
  // the fact that the first line must be at the very beginning
  // is not a very big deal, since we can just strip all whitespace until
  // we reach an ascii character
  // but if there is a easy way to fix this in the grammar, then that is the
  // better option
	match config_grammar::content("# A comment
		bindsym LeftGui+1 workspace 1

bindsym $super+$rand exec 'termite --config $HOME/.config/termite/config'
   bindsym RightGui+2 workspace 2 # this is another comment

set $super Ctrl+Mod4
  # This is a comment
set $ws0 '0:emp'
bindsym RightGui+3 workspace $ws0
bindsym Hello+3 workspace 5

bindsym $super+$left focus left
bindsym $super+Ctrl focus down
bindsym $super+$up focus up
bindsym $super+$right focus right
bindsym $super+1 workspace $ws0

exec --no-startup-id 'nm-applet'

set $left h

"){
		Ok(r) => println!("Parsed as: {:?}", r),
		Err(e) => println!("Parse error: {}", e),
	};

}

#[cfg(test)]
#[test]
fn test_parser() {
  let parser = match Parser::new("/home/insi/Projects/ruwm/src/config_parser/config.test") {
    Ok(s) => s,
    Err(e) => panic!("{}", e)
  };

  println!("Parser: {}", parser.to_string());
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};

type FeatureName = String;

enum Data {
    String(String),
    RangeData((i64, i64)),
}

struct Entry {
    features: HashSet<FeatureName>,
    data: HashMap<FeatureName, Data>,
}

struct EntrySet(Vec<Entry>);

struct DTree {
    thisFork: FeatureName,
    children: Vec<Box<DTree>>,
}

impl DTree {
    fn new() -> Self {
        DTree {
            thisFork: String::new(),
            children: vec![],
        }
    }
}

type DTreeAlgo = fn(EntrySet, HashSet<FeatureName>, FeatureName) -> DTree;

//only support string now
fn gini(datas: EntrySet, feature: FeatureName, target: FeatureName) -> DTree {}

fn c4_5(datas: EntrySet, rest_features: HashSet<FeatureName>, target: FeatureName) -> DTree {
    DTree::new()
}

fn new_tree(datas: EntrySet, target: FeatureName, algo: DTreeAlgo) -> DTree {
    DTree::new()
}
<commit_msg>a bit<commit_after>use std::collections::{HashMap, HashSet};

type FeatureName = String;

#[derive(PartialEq, Eq, Hash)]
enum Data {
    String(String),
    RangeData((i64, i64)),
}

#[derive(PartialEq, Eq)]
struct Entry {
    features: HashSet<FeatureName>,
    data: HashMap<FeatureName, Data>,
}

struct EntrySet(Vec<Entry>);

struct DTree {
    thisFork: FeatureName,
    children: Vec<Box<DTree>>,
}

impl DTree {
    fn new() -> Self {
        DTree {
            thisFork: String::new(),
            children: vec![],
        }
    }
}

type DTreeAlgo = fn(EntrySet, HashSet<FeatureName>, FeatureName) -> DTree;

//only support string now
fn gini(datas: &EntrySet, feature: &FeatureName, target: FeatureName) -> DTree {
    let mut record: HashMap<&Data, HashMap<&Data, u32>> = HashMap::new();
    let EntrySet(data_set) = datas;

    for data in data_set {
        let this_entry_of_target = data.data.get(&target);
        if let Some(record.get_mut(data.data))
        record.insert(
            data.data.get(feature).unwrap(),
            if data.data.get(target)
            record.get(data.data.get(feature).unwrap()).unwrap_or(&0) + 1,
        );
    }
}

fn c4_5(datas: EntrySet, rest_features: HashSet<FeatureName>, target: FeatureName) -> DTree {
    DTree::new()
}

fn new_tree(datas: EntrySet, target: FeatureName, algo: DTreeAlgo) -> DTree {
    DTree::new()
}
<|endoftext|>