<commit_before>// Note: More specifically this lint is largely inspired (aka copied) from
// *rustc*'s
// [`missing_doc`].
//
// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246
//

use crate::utils::span_lint;
use if_chain::if_chain;
use rustc::hir;
use rustc::impl_lint_pass;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use rustc_session::declare_tool_lint;
use syntax::ast::{self, MetaItem, MetaItemKind};
use syntax::attr;
use syntax::source_map::Span;

declare_clippy_lint! {
    /// **What it does:** Warns if there is missing doc for any documentable item
    /// (public or private).
    ///
    /// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`
    /// allowed-by-default lint for
    /// public members, but has no way to enforce documentation of private items.
    /// This lint fixes that.
    ///
    /// **Known problems:** None.
    pub MISSING_DOCS_IN_PRIVATE_ITEMS,
    restriction,
    "detects missing documentation for public and private members"
}

pub struct MissingDoc {
    /// Stack of whether #[doc(hidden)] is set
    /// at each level which has lint attributes.
    doc_hidden_stack: Vec<bool>,
}

impl ::std::default::Default for MissingDoc {
    #[must_use]
    fn default() -> Self {
        Self::new()
    }
}

impl MissingDoc {
    #[must_use]
    pub fn new() -> Self {
        Self {
            doc_hidden_stack: vec![false],
        }
    }

    fn doc_hidden(&self) -> bool {
        *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
    }

    fn has_include(meta: Option<MetaItem>) -> bool {
        if_chain! {
            if let Some(meta) = meta;
            if let MetaItemKind::List(list) = meta.kind;
            if let Some(meta) = list.get(0);
            if let Some(name) = meta.ident();
            then {
                name.as_str() == "include"
            } else {
                false
            }
        }
    }

    fn check_missing_docs_attrs(
        &self,
        cx: &LateContext<'_, '_>,
        attrs: &[ast::Attribute],
        sp: Span,
        desc: &'static str,
    ) {
        // If we're building a test harness, then warning about
        // documentation is probably not really relevant right now.
        if cx.sess().opts.test {
            return;
        }

        // `#[doc(hidden)]` disables missing_docs check.
        if self.doc_hidden() {
            return;
        }

        if sp.from_expansion() {
            return;
        }

        let has_doc = attrs
            .iter()
            .any(|a| a.check_name(sym!(doc)) && (a.is_value_str() || Self::has_include(a.meta())));
        if !has_doc {
            span_lint(
                cx,
                MISSING_DOCS_IN_PRIVATE_ITEMS,
                sp,
                &format!("missing documentation for {}", desc),
            );
        }
    }
}

impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
    fn enter_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, attrs: &'tcx [ast::Attribute]) {
        let doc_hidden = self.doc_hidden()
            || attrs.iter().any(|attr| {
                attr.check_name(sym!(doc))
                    && match attr.meta_item_list() {
                        None => false,
                        Some(l) => attr::list_contains_name(&l[..], sym!(hidden)),
                    }
            });
        self.doc_hidden_stack.push(doc_hidden);
    }

    fn exit_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx [ast::Attribute]) {
        self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
    }

    fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate<'_>) {
        self.check_missing_docs_attrs(cx, &krate.attrs, krate.span, "crate");
    }

    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item<'_>) {
        let desc = match it.kind {
            hir::ItemKind::Const(..) => "a constant",
            hir::ItemKind::Enum(..) => "an enum",
            hir::ItemKind::Fn(..) => {
                // ignore main()
                if it.ident.name == sym!(main) {
                    let def_id = cx.tcx.hir().local_def_id(it.hir_id);
                    let def_key = cx.tcx.hir().def_key(def_id);
                    if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
                        return;
                    }
                }
                "a function"
            },
            hir::ItemKind::Mod(..) => "a module",
            hir::ItemKind::Static(..) => "a static",
            hir::ItemKind::Struct(..) => "a struct",
            hir::ItemKind::Trait(..) => "a trait",
            hir::ItemKind::TraitAlias(..) => "a trait alias",
            hir::ItemKind::TyAlias(..) => "a type alias",
            hir::ItemKind::Union(..) => "a union",
            hir::ItemKind::OpaqueTy(..) => "an existential type",
            hir::ItemKind::ExternCrate(..)
            | hir::ItemKind::ForeignMod(..)
            | hir::ItemKind::GlobalAsm(..)
            | hir::ItemKind::Impl(..)
            | hir::ItemKind::Use(..) => return,
        };

        self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc);
    }

    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
        let desc = match trait_item.kind {
            hir::TraitItemKind::Const(..) => "an associated constant",
            hir::TraitItemKind::Method(..) => "a trait method",
            hir::TraitItemKind::Type(..) => "an associated type",
        };

        self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc);
    }

    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
        // If the method is an impl for a trait, don't doc.
        let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
        match cx.tcx.associated_item(def_id).container {
            ty::TraitContainer(_) => return,
            ty::ImplContainer(cid) => {
                if cx.tcx.impl_trait_ref(cid).is_some() {
                    return;
                }
            },
        }

        let desc = match impl_item.kind {
            hir::ImplItemKind::Const(..) => "an associated constant",
            hir::ImplItemKind::Method(..) => "a method",
            hir::ImplItemKind::TyAlias(_) => "an associated type",
            hir::ImplItemKind::OpaqueTy(_) => "an existential type",
        };
        self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
    }

    fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, sf: &'tcx hir::StructField<'_>) {
        if !sf.is_positional() {
            self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field");
        }
    }

    fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, v: &'tcx hir::Variant<'_>) {
        self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant");
    }
}
<commit_msg>Auto merge of #4967 - JohnTitor:update-test, r=phansch<commit_after>// Note: More specifically this lint is largely inspired (aka copied) from
// *rustc*'s
// [`missing_doc`].
//
// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246
//

use crate::utils::span_lint;
use if_chain::if_chain;
use rustc::hir;
use rustc::impl_lint_pass;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use rustc_session::declare_tool_lint;
use syntax::ast::{self, MetaItem, MetaItemKind};
use syntax::attr;
use syntax::source_map::Span;

declare_clippy_lint! {
    /// **What it does:** Warns if there is missing doc for any documentable item
    /// (public or private).
    ///
    /// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`
    /// allowed-by-default lint for
    /// public members, but has no way to enforce documentation of private items.
    /// This lint fixes that.
    ///
    /// **Known problems:** None.
    pub MISSING_DOCS_IN_PRIVATE_ITEMS,
    restriction,
    "detects missing documentation for public and private members"
}

pub struct MissingDoc {
    /// Stack of whether #[doc(hidden)] is set
    /// at each level which has lint attributes.
    doc_hidden_stack: Vec<bool>,
}

impl ::std::default::Default for MissingDoc {
    #[must_use]
    fn default() -> Self {
        Self::new()
    }
}

impl MissingDoc {
    #[must_use]
    pub fn new() -> Self {
        Self {
            doc_hidden_stack: vec![false],
        }
    }

    fn doc_hidden(&self) -> bool {
        *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
    }

    fn has_include(meta: Option<MetaItem>) -> bool {
        if_chain! {
            if let Some(meta) = meta;
            if let MetaItemKind::List(list) = meta.kind;
            if let Some(meta) = list.get(0);
            if let Some(name) = meta.ident();
            then {
                name.as_str() == "include"
            } else {
                false
            }
        }
    }

    fn check_missing_docs_attrs(
        &self,
        cx: &LateContext<'_, '_>,
        attrs: &[ast::Attribute],
        sp: Span,
        desc: &'static str,
    ) {
        // If we're building a test harness, then warning about
        // documentation is probably not really relevant right now.
        if cx.sess().opts.test {
            return;
        }

        // `#[doc(hidden)]` disables missing_docs check.
        if self.doc_hidden() {
            return;
        }

        if sp.from_expansion() {
            return;
        }

        let has_doc = attrs
            .iter()
            .any(|a| a.is_doc_comment() || a.doc_str().is_some() || a.is_value_str() || Self::has_include(a.meta()));
        if !has_doc {
            span_lint(
                cx,
                MISSING_DOCS_IN_PRIVATE_ITEMS,
                sp,
                &format!("missing documentation for {}", desc),
            );
        }
    }
}

impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
    fn enter_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, attrs: &'tcx [ast::Attribute]) {
        let doc_hidden = self.doc_hidden()
            || attrs.iter().any(|attr| {
                attr.check_name(sym!(doc))
                    && match attr.meta_item_list() {
                        None => false,
                        Some(l) => attr::list_contains_name(&l[..], sym!(hidden)),
                    }
            });
        self.doc_hidden_stack.push(doc_hidden);
    }

    fn exit_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx [ast::Attribute]) {
        self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
    }

    fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate<'_>) {
        self.check_missing_docs_attrs(cx, &krate.attrs, krate.span, "crate");
    }

    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item<'_>) {
        let desc = match it.kind {
            hir::ItemKind::Const(..) => "a constant",
            hir::ItemKind::Enum(..) => "an enum",
            hir::ItemKind::Fn(..) => {
                // ignore main()
                if it.ident.name == sym!(main) {
                    let def_id = cx.tcx.hir().local_def_id(it.hir_id);
                    let def_key = cx.tcx.hir().def_key(def_id);
                    if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
                        return;
                    }
                }
                "a function"
            },
            hir::ItemKind::Mod(..) => "a module",
            hir::ItemKind::Static(..) => "a static",
            hir::ItemKind::Struct(..) => "a struct",
            hir::ItemKind::Trait(..) => "a trait",
            hir::ItemKind::TraitAlias(..) => "a trait alias",
            hir::ItemKind::TyAlias(..) => "a type alias",
            hir::ItemKind::Union(..) => "a union",
            hir::ItemKind::OpaqueTy(..) => "an existential type",
            hir::ItemKind::ExternCrate(..)
            | hir::ItemKind::ForeignMod(..)
            | hir::ItemKind::GlobalAsm(..)
            | hir::ItemKind::Impl(..)
            | hir::ItemKind::Use(..) => return,
        };

        self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc);
    }

    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
        let desc = match trait_item.kind {
            hir::TraitItemKind::Const(..) => "an associated constant",
            hir::TraitItemKind::Method(..) => "a trait method",
            hir::TraitItemKind::Type(..) => "an associated type",
        };

        self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc);
    }

    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
        // If the method is an impl for a trait, don't doc.
        let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
        match cx.tcx.associated_item(def_id).container {
            ty::TraitContainer(_) => return,
            ty::ImplContainer(cid) => {
                if cx.tcx.impl_trait_ref(cid).is_some() {
                    return;
                }
            },
        }

        let desc = match impl_item.kind {
            hir::ImplItemKind::Const(..) => "an associated constant",
            hir::ImplItemKind::Method(..) => "a method",
            hir::ImplItemKind::TyAlias(_) => "an associated type",
            hir::ImplItemKind::OpaqueTy(_) => "an existential type",
        };
        self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
    }

    fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, sf: &'tcx hir::StructField<'_>) {
        if !sf.is_positional() {
            self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field");
        }
    }

    fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, v: &'tcx hir::Variant<'_>) {
        self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant");
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    data_stream::{DataStream, DataStreamId, DataStreamListener},
    error::Error,
    logging::{LogEntry, LogEvent, LogSchema},
    metrics,
    metrics::increment_counter,
    streaming_client::{
        StreamRequest, StreamRequestMessage, StreamingServiceListener, TerminateStreamRequest,
    },
};
use aptos_config::config::DataStreamingServiceConfig;
use aptos_data_client::{AptosDataClient, GlobalDataSummary, OptimalChunkSizes};
use aptos_id_generator::{IdGenerator, U64IdGenerator};
use aptos_logger::prelude::*;
use futures::StreamExt;
use std::{collections::HashMap, sync::Arc, time::Duration};
use tokio::time::interval;
use tokio_stream::wrappers::IntervalStream;

// Useful constants for the Data Streaming Service
const REFRESH_ERROR_LOG_FREQ_SECS: u64 = 1;

/// The data streaming service that responds to data stream requests.
pub struct DataStreamingService<T> {
    // The configuration for this streaming service.
    config: DataStreamingServiceConfig,

    // The data client through which to fetch data from the Diem network
    aptos_data_client: T,

    // Cached global data summary
    global_data_summary: GlobalDataSummary,

    // All requested data streams from clients
    data_streams: HashMap<DataStreamId, DataStream<T>>,

    // The listener through which to hear new client stream requests
    stream_requests: StreamingServiceListener,

    // Unique ID generators to maintain unique IDs across streams
    stream_id_generator: U64IdGenerator,
    notification_id_generator: Arc<U64IdGenerator>,
}

impl<T: AptosDataClient + Send + Clone + 'static> DataStreamingService<T> {
    pub fn new(
        config: DataStreamingServiceConfig,
        aptos_data_client: T,
        stream_requests: StreamingServiceListener,
    ) -> Self {
        Self {
            config,
            aptos_data_client,
            global_data_summary: GlobalDataSummary::empty(),
            data_streams: HashMap::new(),
            stream_requests,
            stream_id_generator: U64IdGenerator::new(),
            notification_id_generator: Arc::new(U64IdGenerator::new()),
        }
    }

    /// Starts the dedicated streaming service
    pub async fn start_service(mut self) {
        let mut data_refresh_interval = IntervalStream::new(interval(Duration::from_millis(
            self.config.global_summary_refresh_interval_ms,
        )))
        .fuse();
        let mut progress_check_interval = IntervalStream::new(interval(Duration::from_millis(
            self.config.progress_check_interval_ms,
        )))
        .fuse();

        loop {
            ::futures::select! {
                stream_request = self.stream_requests.select_next_some() => {
                    self.handle_stream_request_message(stream_request);
                }
                _ = data_refresh_interval.select_next_some() => {
                    self.refresh_global_data_summary();
                }
                _ = progress_check_interval.select_next_some() => {
                    self.check_progress_of_all_data_streams();
                }
            }
        }
    }

    /// Handles new stream request messages from clients
    fn handle_stream_request_message(&mut self, request_message: StreamRequestMessage) {
        if let StreamRequest::TerminateStream(request) = request_message.stream_request {
            // Process the feedback request
            if let Err(error) = self.process_terminate_stream_request(&request) {
                error!(LogSchema::new(LogEntry::HandleTerminateRequest)
                    .event(LogEvent::Error)
                    .error(&error));
            }
            return;
        }

        // Process the stream request
        let response = self.process_new_stream_request(&request_message);
        if let Err(error) = &response {
            error!(LogSchema::new(LogEntry::HandleStreamRequest)
                .event(LogEvent::Error)
                .error(error));
        }

        // Send the response to the client
        if let Err(error) = request_message.response_sender.send(response) {
            error!(LogSchema::new(LogEntry::RespondToStreamRequest)
                .event(LogEvent::Error)
                .message(&format!(
                    "Failed to send response for stream request: {:?}",
                    error
                )));
        }
    }

    /// Processes a request for terminating the stream that sent a specific
    /// notification ID.
    fn process_terminate_stream_request(
        &mut self,
        terminate_request: &TerminateStreamRequest,
    ) -> Result<(), Error> {
        // Increment the stream termination counter
        let notification_feedback = &terminate_request.notification_feedback;
        increment_counter(
            &metrics::TERMINATE_DATA_STREAM,
            notification_feedback.get_label().into(),
        );

        // Find the data stream that sent the notification
        let notification_id = &terminate_request.notification_id;
        let data_stream_ids = self.get_all_data_stream_ids();
        for data_stream_id in &data_stream_ids {
            let data_stream = self.get_data_stream(data_stream_id);
            if data_stream.sent_notification(notification_id) {
                info!(LogSchema::new(LogEntry::HandleTerminateRequest)
                    .stream_id(*data_stream_id)
                    .event(LogEvent::Success)
                    .message(&format!(
                        "Terminating the stream that sent notification ID: {:?}. Feedback: {:?}",
                        notification_id, notification_feedback,
                    )));
                data_stream.handle_notification_feedback(notification_id, notification_feedback)?;
                self.data_streams.remove(notification_id);
                return Ok(());
            }
        }

        panic!(
            "Unable to find the stream that sent notification ID: {:?}. Feedback: {:?}",
            notification_id, notification_feedback,
        );
    }

    /// Creates a new stream and ensures the data for that stream is available
    fn process_new_stream_request(
        &mut self,
        request_message: &StreamRequestMessage,
    ) -> Result<DataStreamListener, Error> {
        // Increment the stream creation counter
        increment_counter(
            &metrics::CREATE_DATA_STREAM,
            request_message.stream_request.get_label().into(),
        );

        // Refresh the cached global data summary
        self.refresh_global_data_summary();

        // Create a new data stream
        let stream_id = self.stream_id_generator.next();
        let (data_stream, stream_listener) = DataStream::new(
            self.config,
            stream_id,
            &request_message.stream_request,
            self.aptos_data_client.clone(),
            self.notification_id_generator.clone(),
            &self.global_data_summary.advertised_data,
        )?;

        // Verify the data stream can be fulfilled using the currently advertised data
        data_stream.ensure_data_is_available(&self.global_data_summary.advertised_data)?;

        // Store the data stream internally
        if self.data_streams.insert(stream_id, data_stream).is_some() {
            panic!(
                "Duplicate data stream found! This should not occur! ID: {:?}",
                stream_id,
            );
        }
        info!(LogSchema::new(LogEntry::HandleStreamRequest)
            .stream_id(stream_id)
            .event(LogEvent::Success)
            .message(&format!(
                "Stream created for request: {:?}",
                request_message
            )));

        // Return the listener
        Ok(stream_listener)
    }

    /// Refreshes the global data summary by communicating with the Diem data client
    fn refresh_global_data_summary(&mut self) {
        if let Err(error) = self.fetch_global_data_summary() {
            increment_counter(
                &metrics::GLOBAL_DATA_SUMMARY_ERROR,
                error.get_label().into(),
            );
            sample!(
                SampleRate::Duration(Duration::from_secs(REFRESH_ERROR_LOG_FREQ_SECS)),
                error!(LogSchema::new(LogEntry::RefreshGlobalData)
                    .event(LogEvent::Error)
                    .error(&error))
            );
        }
    }

    fn fetch_global_data_summary(&mut self) -> Result<(), Error> {
        let global_data_summary = self.aptos_data_client.get_global_data_summary();
        verify_optimal_chunk_sizes(&global_data_summary.optimal_chunk_sizes)?;
        self.global_data_summary = global_data_summary;
        Ok(())
    }

    /// Ensures that all existing data streams are making progress
    fn check_progress_of_all_data_streams(&mut self) {
        let data_stream_ids = self.get_all_data_stream_ids();
        for data_stream_id in &data_stream_ids {
            if let Err(error) = self.update_progress_of_data_stream(data_stream_id) {
                increment_counter(
                    &metrics::CHECK_STREAM_PROGRESS_ERROR,
                    error.get_label().into(),
                );
                error!(LogSchema::new(LogEntry::CheckStreamProgress)
                    .stream_id(*data_stream_id)
                    .event(LogEvent::Error)
                    .error(&error));
            }
        }
    }

    /// Ensures that a data stream has in-flight data requests and handles
    /// any new responses that have arrived since we last checked.
    fn update_progress_of_data_stream(
        &mut self,
        data_stream_id: &DataStreamId,
    ) -> Result<(), Error> {
        let global_data_summary = self.global_data_summary.clone();

        let data_stream = self.get_data_stream_mut(data_stream_id);
        if !data_stream.data_requests_initialized() {
            // Initialize the request batch by sending out data client requests
            data_stream.initialize_data_requests(global_data_summary)?;
            info!(
                (LogSchema::new(LogEntry::InitializeStream)
                    .stream_id(*data_stream_id)
                    .event(LogEvent::Success)
                    .message("Data stream initialized."))
            );
        } else {
            // Process any data client requests that have received responses
            data_stream.process_data_responses(global_data_summary)?;
        }

        Ok(())
    }

    fn get_all_data_stream_ids(&self) -> Vec<DataStreamId> {
        self.data_streams
            .keys()
            .cloned()
            .collect::<Vec<DataStreamId>>()
    }

    /// Returns the data stream associated with the given `data_stream_id`.
    /// Note: this method assumes the caller has already verified the stream exists.
    fn get_data_stream(&self, data_stream_id: &DataStreamId) -> &DataStream<T> {
        self.data_streams.get(data_stream_id).unwrap_or_else(|| {
            panic!(
                "Expected a data stream with ID: {:?}, but found None!",
                data_stream_id
            )
        })
    }

    /// Returns the data stream associated with the given `data_stream_id`.
    /// Note: this method assumes the caller has already verified the stream exists.
    fn get_data_stream_mut(&mut self, data_stream_id: &DataStreamId) -> &mut DataStream<T> {
        self.data_streams
            .get_mut(data_stream_id)
            .unwrap_or_else(|| {
                panic!(
                    "Expected a data stream with ID: {:?}, but found None!",
                    data_stream_id
                )
            })
    }
}

/// Verifies that all optimal chunk sizes are valid (i.e., not zero). Returns an
/// error if a chunk size is 0.
fn verify_optimal_chunk_sizes(optimal_chunk_sizes: &OptimalChunkSizes) -> Result<(), Error> {
    if optimal_chunk_sizes.account_states_chunk_size == 0
        || optimal_chunk_sizes.epoch_chunk_size == 0
        || optimal_chunk_sizes.transaction_chunk_size == 0
        || optimal_chunk_sizes.transaction_output_chunk_size == 0
    {
        Err(Error::AptosDataClientResponseIsInvalid(format!(
            "Found at least one optimal chunk size of zero: {:?}",
            optimal_chunk_sizes
        )))
    } else {
        Ok(())
    }
}
<commit_msg>[State Sync] Improve the error log for empty global data summaries.<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    data_stream::{DataStream, DataStreamId, DataStreamListener},
    error::Error,
    logging::{LogEntry, LogEvent, LogSchema},
    metrics,
    metrics::increment_counter,
    streaming_client::{
        StreamRequest, StreamRequestMessage, StreamingServiceListener, TerminateStreamRequest,
    },
};
use aptos_config::config::DataStreamingServiceConfig;
use aptos_data_client::{AptosDataClient, GlobalDataSummary, OptimalChunkSizes};
use aptos_id_generator::{IdGenerator, U64IdGenerator};
use aptos_logger::prelude::*;
use futures::StreamExt;
use std::{collections::HashMap, sync::Arc, time::Duration};
use tokio::time::interval;
use tokio_stream::wrappers::IntervalStream;

// Useful constants for the Data Streaming Service
const GLOBAL_DATA_REFRESH_LOG_FREQ_SECS: u64 = 1;

/// The data streaming service that responds to data stream requests.
pub struct DataStreamingService<T> {
    // The configuration for this streaming service.
    config: DataStreamingServiceConfig,

    // The data client through which to fetch data from the Diem network
    aptos_data_client: T,

    // Cached global data summary
    global_data_summary: GlobalDataSummary,

    // All requested data streams from clients
    data_streams: HashMap<DataStreamId, DataStream<T>>,

    // The listener through which to hear new client stream requests
    stream_requests: StreamingServiceListener,

    // Unique ID generators to maintain unique IDs across streams
    stream_id_generator: U64IdGenerator,
    notification_id_generator: Arc<U64IdGenerator>,
}

impl<T: AptosDataClient + Send + Clone + 'static> DataStreamingService<T> {
    pub fn new(
        config: DataStreamingServiceConfig,
        aptos_data_client: T,
        stream_requests: StreamingServiceListener,
    ) -> Self {
        Self {
            config,
            aptos_data_client,
            global_data_summary: GlobalDataSummary::empty(),
            data_streams: HashMap::new(),
            stream_requests,
            stream_id_generator: U64IdGenerator::new(),
            notification_id_generator: Arc::new(U64IdGenerator::new()),
        }
    }

    /// Starts the dedicated streaming service
    pub async fn start_service(mut self) {
        let mut data_refresh_interval = IntervalStream::new(interval(Duration::from_millis(
            self.config.global_summary_refresh_interval_ms,
        )))
        .fuse();
        let mut progress_check_interval = IntervalStream::new(interval(Duration::from_millis(
            self.config.progress_check_interval_ms,
        )))
        .fuse();

        loop {
            ::futures::select! {
                stream_request = self.stream_requests.select_next_some() => {
                    self.handle_stream_request_message(stream_request);
                }
                _ = data_refresh_interval.select_next_some() => {
                    self.refresh_global_data_summary();
                }
                _ = progress_check_interval.select_next_some() => {
                    self.check_progress_of_all_data_streams();
                }
            }
        }
    }

    /// Handles new stream request messages from clients
    fn handle_stream_request_message(&mut self, request_message: StreamRequestMessage) {
        if let StreamRequest::TerminateStream(request) = request_message.stream_request {
            // Process the feedback request
            if let Err(error) = self.process_terminate_stream_request(&request) {
                error!(LogSchema::new(LogEntry::HandleTerminateRequest)
                    .event(LogEvent::Error)
                    .error(&error));
            }
            return;
        }

        // Process the stream request
        let response = self.process_new_stream_request(&request_message);
        if let Err(error) = &response {
            error!(LogSchema::new(LogEntry::HandleStreamRequest)
                .event(LogEvent::Error)
                .error(error));
        }

        // Send the response to the client
        if let Err(error) = request_message.response_sender.send(response) {
            error!(LogSchema::new(LogEntry::RespondToStreamRequest)
                .event(LogEvent::Error)
                .message(&format!(
                    "Failed to send response for stream request: {:?}",
                    error
                )));
        }
    }

    /// Processes a request for terminating the stream that sent a specific
    /// notification ID.
    fn process_terminate_stream_request(
        &mut self,
        terminate_request: &TerminateStreamRequest,
    ) -> Result<(), Error> {
        // Increment the stream termination counter
        let notification_feedback = &terminate_request.notification_feedback;
        increment_counter(
            &metrics::TERMINATE_DATA_STREAM,
            notification_feedback.get_label().into(),
        );

        // Find the data stream that sent the notification
        let notification_id = &terminate_request.notification_id;
        let data_stream_ids = self.get_all_data_stream_ids();
        for data_stream_id in &data_stream_ids {
            let data_stream = self.get_data_stream(data_stream_id);
            if data_stream.sent_notification(notification_id) {
                info!(LogSchema::new(LogEntry::HandleTerminateRequest)
                    .stream_id(*data_stream_id)
                    .event(LogEvent::Success)
                    .message(&format!(
                        "Terminating the stream that sent notification ID: {:?}. Feedback: {:?}",
                        notification_id, notification_feedback,
                    )));
                data_stream.handle_notification_feedback(notification_id, notification_feedback)?;
                self.data_streams.remove(notification_id);
                return Ok(());
            }
        }

        panic!(
            "Unable to find the stream that sent notification ID: {:?}. Feedback: {:?}",
            notification_id, notification_feedback,
        );
    }

    /// Creates a new stream and ensures the data for that stream is available
    fn process_new_stream_request(
        &mut self,
        request_message: &StreamRequestMessage,
    ) -> Result<DataStreamListener, Error> {
        // Increment the stream creation counter
        increment_counter(
            &metrics::CREATE_DATA_STREAM,
            request_message.stream_request.get_label().into(),
        );

        // Refresh the cached global data summary
        self.refresh_global_data_summary();

        // Create a new data stream
        let stream_id = self.stream_id_generator.next();
        let (data_stream, stream_listener) = DataStream::new(
            self.config,
            stream_id,
            &request_message.stream_request,
            self.aptos_data_client.clone(),
            self.notification_id_generator.clone(),
            &self.global_data_summary.advertised_data,
        )?;

        // Verify the data stream can be fulfilled using the currently advertised data
        data_stream.ensure_data_is_available(&self.global_data_summary.advertised_data)?;

        // Store the data stream internally
        if self.data_streams.insert(stream_id, data_stream).is_some() {
            panic!(
                "Duplicate data stream found! This should not occur! ID: {:?}",
                stream_id,
            );
        }
        info!(LogSchema::new(LogEntry::HandleStreamRequest)
            .stream_id(stream_id)
            .event(LogEvent::Success)
            .message(&format!(
                "Stream created for request: {:?}",
                request_message
            )));

        // Return the listener
        Ok(stream_listener)
    }

    /// Refreshes the global data summary by communicating with the Diem data client
    fn refresh_global_data_summary(&mut self) {
        if let Err(error) = self.fetch_global_data_summary() {
            increment_counter(
                &metrics::GLOBAL_DATA_SUMMARY_ERROR,
                error.get_label().into(),
            );
            sample!(
                SampleRate::Duration(Duration::from_secs(GLOBAL_DATA_REFRESH_LOG_FREQ_SECS)),
                error!(LogSchema::new(LogEntry::RefreshGlobalData)
                    .event(LogEvent::Error)
                    .error(&error))
            );
        }
    }

    fn fetch_global_data_summary(&mut self) -> Result<(), Error> {
        let global_data_summary = self.aptos_data_client.get_global_data_summary();
        if global_data_summary.is_empty() {
            sample!(
                SampleRate::Duration(Duration::from_secs(GLOBAL_DATA_REFRESH_LOG_FREQ_SECS)),
                info!(LogSchema::new(LogEntry::RefreshGlobalData)
                    .message("Latest global data summary is empty."))
            );
        } else {
            verify_optimal_chunk_sizes(&global_data_summary.optimal_chunk_sizes)?;
            self.global_data_summary = global_data_summary;
        }

        Ok(())
    }

    /// Ensures that all existing data streams are making progress
    fn check_progress_of_all_data_streams(&mut self) {
        let data_stream_ids = self.get_all_data_stream_ids();
        for data_stream_id in &data_stream_ids {
            if let Err(error) = self.update_progress_of_data_stream(data_stream_id) {
                increment_counter(
                    &metrics::CHECK_STREAM_PROGRESS_ERROR,
                    error.get_label().into(),
                );
                error!(LogSchema::new(LogEntry::CheckStreamProgress)
                    .stream_id(*data_stream_id)
                    .event(LogEvent::Error)
                    .error(&error));
            }
        }
    }

    /// Ensures that a data stream has in-flight data requests and handles
    /// any new responses that have arrived since we last checked.
    fn update_progress_of_data_stream(
        &mut self,
        data_stream_id: &DataStreamId,
    ) -> Result<(), Error> {
        let global_data_summary = self.global_data_summary.clone();

        let data_stream = self.get_data_stream_mut(data_stream_id);
        if !data_stream.data_requests_initialized() {
            // Initialize the request batch by sending out data client requests
            data_stream.initialize_data_requests(global_data_summary)?;
            info!(
                (LogSchema::new(LogEntry::InitializeStream)
                    .stream_id(*data_stream_id)
                    .event(LogEvent::Success)
                    .message("Data stream initialized."))
            );
        } else {
            // Process any data client requests that have received responses
            data_stream.process_data_responses(global_data_summary)?;
        }

        Ok(())
    }

    fn get_all_data_stream_ids(&self) -> Vec<DataStreamId> {
        self.data_streams
            .keys()
            .cloned()
            .collect::<Vec<DataStreamId>>()
    }

    /// Returns the data stream associated with the given `data_stream_id`.
    /// Note: this method assumes the caller has already verified the stream exists.
    fn get_data_stream(&self, data_stream_id: &DataStreamId) -> &DataStream<T> {
        self.data_streams.get(data_stream_id).unwrap_or_else(|| {
            panic!(
                "Expected a data stream with ID: {:?}, but found None!",
                data_stream_id
            )
        })
    }

    /// Returns the data stream associated with the given `data_stream_id`.
    /// Note: this method assumes the caller has already verified the stream exists.
    fn get_data_stream_mut(&mut self, data_stream_id: &DataStreamId) -> &mut DataStream<T> {
        self.data_streams
            .get_mut(data_stream_id)
            .unwrap_or_else(|| {
                panic!(
                    "Expected a data stream with ID: {:?}, but found None!",
                    data_stream_id
                )
            })
    }
}

/// Verifies that all optimal chunk sizes are valid (i.e., not zero). Returns an
/// error if a chunk size is 0.
fn verify_optimal_chunk_sizes(optimal_chunk_sizes: &OptimalChunkSizes) -> Result<(), Error> {
    if optimal_chunk_sizes.account_states_chunk_size == 0
        || optimal_chunk_sizes.epoch_chunk_size == 0
        || optimal_chunk_sizes.transaction_chunk_size == 0
        || optimal_chunk_sizes.transaction_output_chunk_size == 0
    {
        Err(Error::AptosDataClientResponseIsInvalid(format!(
            "Found at least one optimal chunk size of zero: {:?}",
            optimal_chunk_sizes
        )))
    } else {
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::process::Command;
use std::time::{Duration, Instant};

fn main() {
    let mut args = env::args_os().skip(1).collect::<Vec<_>>();
    let rustc = env::var_os("RUSTC_REAL").unwrap();

    if let Some(count) = env::var("RUSTC_THREAD_COUNT")
        .ok()
        .and_then(|v| v.parse::<u32>().ok())
    {
        args.push(std::ffi::OsString::from(format!("-Zthreads={}", count)));
    }

    args.push(std::ffi::OsString::from("-Adeprecated"));

    if let Some(pos) = args.iter().position(|arg| arg == "--wrap-rustc-with") {
        // Strip out the flag and its argument, and run rustc under the wrapper
        // program named by the argument.
        args.remove(pos);
        let wrapper = args.remove(pos);
        let wrapper = wrapper.to_str().unwrap();

        raise_priority();

        match wrapper {
            "perf-stat" | "perf-stat-self-profile" => {
                let mut cmd = Command::new("perf");
                let has_perf = cmd.output().is_ok();
                assert!(has_perf);
                cmd.arg("stat")
                    .arg("-x;")
                    .arg("-e")
                    .arg("instructions:u,cycles:u,task-clock,cpu-clock,faults")
                    .arg("--log-fd")
                    .arg("1")
                    .arg(&rustc)
                    .args(&args);

                let prof_out_dir = std::env::current_dir().unwrap().join("self-profile-output");
                if wrapper == "perf-stat-self-profile" {
                    cmd.arg(&format!(
                        "-Zself-profile={}",
                        prof_out_dir.to_str().unwrap()
                    ));
                    cmd.arg("-Zself-profile-events=all");
                    let _ = std::fs::remove_dir_all(&prof_out_dir);
                    let _ = std::fs::create_dir_all(&prof_out_dir);
                }

                let start = Instant::now();
                let status = cmd.status().expect("failed to spawn");
                let dur = start.elapsed();
                assert!(status.success());
                print_memory();
                print_time(dur);
                if wrapper == "perf-stat-self-profile" {
                    let crate_name = args
                        .windows(2)
                        .find(|args| args[0] == "--crate-name")
                        .and_then(|args| args[1].to_str())
                        .expect("rustc to be invoked with crate name");
                    let mut prefix = None;
                    // We don't know the pid of rustc, and can't easily get it -- we only know the
                    // `perf` pid. So just blindly look in the directory to hopefully find it.
                    for entry in std::fs::read_dir(&prof_out_dir).unwrap() {
                        let entry = entry.unwrap();
                        if entry
                            .file_name()
                            .to_str()
                            .map_or(false, |s| s.starts_with(crate_name))
                        {
                            let file = entry.file_name().to_str().unwrap().to_owned();
                            let new_prefix = Some(file[..file.find('.').unwrap()].to_owned());
                            assert!(
                                prefix.is_none() || prefix == new_prefix,
                                "prefix={:?}, new_prefix={:?}",
                                prefix,
                                new_prefix
                            );
                            prefix = new_prefix;
                        }
                    }
                    let prefix = prefix.expect(&format!("found prefix {:?}", prof_out_dir));
                    // FIXME: it'd be great to have a less generic name for this;
                    // we should think about renaming the binary in measureme to measureme, such
                    // that the command is `measureme summarize ...`.
                    let mut cmd = Command::new("summarize");
                    cmd.current_dir(&prof_out_dir);
                    cmd.arg("summarize").arg("--json");
                    cmd.arg(&prefix);
                    let status = cmd.status().expect(&format!(
                        "summarize spawned successfully in {:?}",
                        prof_out_dir
                    ));
                    assert!(
                        status.success(),
                        "summarize failed in {:?}; prefix is {:?}",
                        prof_out_dir,
                        prefix
                    );
                    let json =
                        std::fs::read_to_string(prof_out_dir.join(&format!("{}.json", prefix)))
                            .expect("able to read JSON output");
                    println!("!self-profile-output:{}", json);
                }
            }

            "time-passes" => {
                let mut cmd = Command::new(&rustc);
                cmd.arg("-Ztime-passes").args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "perf-record" => {
                let mut cmd = Command::new("perf");
                let has_perf = cmd.output().is_ok();
                assert!(has_perf);
                cmd.arg("record")
                    .arg("--call-graph=dwarf")
                    .arg("--output=perf")
                    .arg("--freq=99")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "oprofile" => {
                let mut cmd = Command::new("operf");
                let has_oprofile = cmd.output().is_ok();
                assert!(has_oprofile);
                // Other possibly useful args: --callgraph, --separate-thread
                cmd.arg("operf").arg(&rustc).args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "cachegrind" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);

                // With --cache-sim=no and --branch-sim=no, Cachegrind just
                // collects instruction counts.
                cmd.arg("--tool=cachegrind")
                    .arg("--cache-sim=no")
                    .arg("--branch-sim=no")
                    .arg("--cachegrind-out-file=cgout")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "callgrind" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);

                // With --cache-sim=no and --branch-sim=no, Callgrind just
                // collects instruction counts.
                cmd.arg("--tool=callgrind")
                    .arg("--cache-sim=no")
                    .arg("--branch-sim=no")
                    .arg("--callgrind-out-file=clgout")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "exp-dhat" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);
                cmd.arg("--tool=exp-dhat")
                    .arg("--show-top-n=500")
                    .arg("--num-callers=4")
                    .arg("--sort-by=tot-blocks-allocd")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "dhat" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);
                cmd.arg("--tool=dhat")
                    .arg("--num-callers=4")
                    .arg("--dhat-out-file=dhout")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "massif" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);
                cmd.arg("--tool=massif")
                    .arg("--heap-admin=0")
                    .arg("--depth=15")
                    .arg("--threshold=0.2")
                    .arg("--massif-out-file=msout")
                    .arg("--alloc-fn=__rdl_alloc")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "eprintln" => {
                let mut cmd = Command::new(&rustc);
                cmd.args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            _ => {
                panic!("unknown wrapper: {}", wrapper);
            }
        }
    } else {
        let mut cmd = Command::new(&rustc);
        cmd.args(&args);
        exec(&mut cmd);
    }
}

#[cfg(unix)]
fn exec(cmd: &mut Command) -> ! {
    use std::os::unix::prelude::*;
    let error = cmd.exec();
    panic!("failed to exec: {}", error);
}

#[cfg(unix)]
fn raise_priority() {
    unsafe {
        // Try to reduce jitter in wall time by increasing our priority to the
        // maximum
        for i in (1..21).rev() {
            let r = libc::setpriority(libc::PRIO_PROCESS as _, libc::getpid() as libc::id_t, -i);
            if r == 0 {
                break;
            }
        }
    }
}

#[cfg(unix)]
fn print_memory() {
    use std::mem;

    unsafe {
        let mut usage = mem::zeroed();
        let r = libc::getrusage(libc::RUSAGE_CHILDREN, &mut usage);
        if r == 0 {
            // for explanation of all the semicolons, see `print_time` below
            println!("{};;max-rss;3;100.00", usage.ru_maxrss);
        }
    }
}

fn print_time(dur: Duration) {
    // Format output the same as `perf stat` in CSV mode, explained at
    // http://man7.org/linux/man-pages/man1/perf-stat.1.html#CSV_FORMAT
    //
    // tl;dr; it's:
    //
    //      $value ; $unit ; $name ; $runtime ; $pct
    println!(
        "{}.{:09};;wall-time;4;100.00",
        dur.as_secs(),
        dur.subsec_nanos()
    );
}

#[cfg(windows)]
fn raise_priority() {}

#[cfg(windows)]
fn print_memory() {}
<commit_msg>Switch back to default set of events collected<commit_after>use std::env;
use std::process::Command;
use std::time::{Duration, Instant};

fn main() {
    let mut args = env::args_os().skip(1).collect::<Vec<_>>();
    let rustc = env::var_os("RUSTC_REAL").unwrap();

    if let Some(count) = env::var("RUSTC_THREAD_COUNT")
        .ok()
        .and_then(|v| v.parse::<u32>().ok())
    {
        args.push(std::ffi::OsString::from(format!("-Zthreads={}", count)));
    }

    args.push(std::ffi::OsString::from("-Adeprecated"));

    if let Some(pos) = args.iter().position(|arg| arg == "--wrap-rustc-with") {
        // Strip out the flag and its argument, and run rustc under the wrapper
        // program named by the argument.
        args.remove(pos);
        let wrapper = args.remove(pos);
        let wrapper = wrapper.to_str().unwrap();

        raise_priority();

        match wrapper {
            "perf-stat" | "perf-stat-self-profile" => {
                let mut cmd = Command::new("perf");
                let has_perf = cmd.output().is_ok();
                assert!(has_perf);
                cmd.arg("stat")
                    .arg("-x;")
                    .arg("-e")
                    .arg("instructions:u,cycles:u,task-clock,cpu-clock,faults")
                    .arg("--log-fd")
                    .arg("1")
                    .arg(&rustc)
                    .args(&args);

                let prof_out_dir = std::env::current_dir().unwrap().join("self-profile-output");
                if wrapper == "perf-stat-self-profile" {
                    cmd.arg(&format!(
                        "-Zself-profile={}",
                        prof_out_dir.to_str().unwrap()
                    ));
                    let _ = std::fs::remove_dir_all(&prof_out_dir);
                    let _ = std::fs::create_dir_all(&prof_out_dir);
                }

                let start = Instant::now();
                let status = cmd.status().expect("failed to spawn");
                let dur = start.elapsed();
                assert!(status.success());
                print_memory();
                print_time(dur);
                if wrapper == "perf-stat-self-profile" {
                    let crate_name = args
                        .windows(2)
                        .find(|args| args[0] == "--crate-name")
                        .and_then(|args| args[1].to_str())
                        .expect("rustc to be invoked with crate name");
                    let mut prefix = None;
                    // We don't know the pid of rustc, and can't easily get it -- we only know the
                    // `perf` pid. So just blindly look in the directory to hopefully find it.
                    for entry in std::fs::read_dir(&prof_out_dir).unwrap() {
                        let entry = entry.unwrap();
                        if entry
                            .file_name()
                            .to_str()
                            .map_or(false, |s| s.starts_with(crate_name))
                        {
                            let file = entry.file_name().to_str().unwrap().to_owned();
                            let new_prefix = Some(file[..file.find('.').unwrap()].to_owned());
                            assert!(
                                prefix.is_none() || prefix == new_prefix,
                                "prefix={:?}, new_prefix={:?}",
                                prefix,
                                new_prefix
                            );
                            prefix = new_prefix;
                        }
                    }
                    let prefix = prefix.expect(&format!("found prefix {:?}", prof_out_dir));
                    // FIXME: it'd be great to have a less generic name for this;
                    // we should think about renaming the binary in measureme to measureme, such
                    // that the command is `measureme summarize ...`.
                    let mut cmd = Command::new("summarize");
                    cmd.current_dir(&prof_out_dir);
                    cmd.arg("summarize").arg("--json");
                    cmd.arg(&prefix);
                    let status = cmd.status().expect(&format!(
                        "summarize spawned successfully in {:?}",
                        prof_out_dir
                    ));
                    assert!(
                        status.success(),
                        "summarize failed in {:?}; prefix is {:?}",
                        prof_out_dir,
                        prefix
                    );
                    let json =
                        std::fs::read_to_string(prof_out_dir.join(&format!("{}.json", prefix)))
                            .expect("able to read JSON output");
                    println!("!self-profile-output:{}", json);
                }
            }

            "time-passes" => {
                let mut cmd = Command::new(&rustc);
                cmd.arg("-Ztime-passes").args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "perf-record" => {
                let mut cmd = Command::new("perf");
                let has_perf = cmd.output().is_ok();
                assert!(has_perf);
                cmd.arg("record")
                    .arg("--call-graph=dwarf")
                    .arg("--output=perf")
                    .arg("--freq=99")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "oprofile" => {
                let mut cmd = Command::new("operf");
                let has_oprofile = cmd.output().is_ok();
                assert!(has_oprofile);
                // Other possibly useful args: --callgraph, --separate-thread
                cmd.arg("operf").arg(&rustc).args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "cachegrind" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);

                // With --cache-sim=no and --branch-sim=no, Cachegrind just
                // collects instruction counts.
                cmd.arg("--tool=cachegrind")
                    .arg("--cache-sim=no")
                    .arg("--branch-sim=no")
                    .arg("--cachegrind-out-file=cgout")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "callgrind" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);

                // With --cache-sim=no and --branch-sim=no, Callgrind just
                // collects instruction counts.
                cmd.arg("--tool=callgrind")
                    .arg("--cache-sim=no")
                    .arg("--branch-sim=no")
                    .arg("--callgrind-out-file=clgout")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "exp-dhat" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);
                cmd.arg("--tool=exp-dhat")
                    .arg("--show-top-n=500")
                    .arg("--num-callers=4")
                    .arg("--sort-by=tot-blocks-allocd")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "dhat" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);
                cmd.arg("--tool=dhat")
                    .arg("--num-callers=4")
                    .arg("--dhat-out-file=dhout")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "massif" => {
                let mut cmd = Command::new("valgrind");
                let has_valgrind = cmd.output().is_ok();
                assert!(has_valgrind);
                cmd.arg("--tool=massif")
                    .arg("--heap-admin=0")
                    .arg("--depth=15")
                    .arg("--threshold=0.2")
                    .arg("--massif-out-file=msout")
                    .arg("--alloc-fn=__rdl_alloc")
                    .arg(&rustc)
                    .args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            "eprintln" => {
                let mut cmd = Command::new(&rustc);
                cmd.args(&args);

                assert!(cmd.status().expect("failed to spawn").success());
            }

            _ => {
                panic!("unknown wrapper: {}", wrapper);
            }
        }
    } else {
        let mut cmd = Command::new(&rustc);
        cmd.args(&args);
        exec(&mut cmd);
    }
}

#[cfg(unix)]
fn exec(cmd: &mut Command) -> ! {
    use std::os::unix::prelude::*;
    let error = cmd.exec();
    panic!("failed to exec: {}", error);
}

#[cfg(unix)]
fn raise_priority() {
    unsafe {
        // Try to reduce jitter in wall time by increasing our priority to the
        // maximum
        for i in (1..21).rev() {
            let r = libc::setpriority(libc::PRIO_PROCESS as _, libc::getpid() as libc::id_t, -i);
            if r == 0 {
                break;
            }
        }
    }
}

#[cfg(unix)]
fn print_memory() {
    use std::mem;

    unsafe {
        let mut usage = mem::zeroed();
        let r = libc::getrusage(libc::RUSAGE_CHILDREN, &mut usage);
        if r == 0 {
            // for explanation of all the semicolons, see `print_time` below
            println!("{};;max-rss;3;100.00", usage.ru_maxrss);
        }
    }
}

fn print_time(dur: Duration) {
    // Format output the same as `perf stat` in CSV mode, explained at
    // http://man7.org/linux/man-pages/man1/perf-stat.1.html#CSV_FORMAT
    //
    // tl;dr; it's:
    //
    //      $value ; $unit ; $name ; $runtime ; $pct
    println!(
        "{}.{:09};;wall-time;4;100.00",
        dur.as_secs(),
        dur.subsec_nanos()
    );
}

#[cfg(windows)]
fn raise_priority() {}

#[cfg(windows)]
fn print_memory() {}
<|endoftext|>


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

//! Resolution values:
//!
//! https://drafts.csswg.org/css-values/#resolution

use cssparser::{Parser, Token};
use parser::{Parse, ParserContext};
use style_traits::{ParseError, StyleParseErrorKind};
use values::CSSFloat;

/// A specified resolution.
#[derive(Clone, Debug, PartialEq, ToCss)]
pub enum Resolution {
    /// Dots per inch.
    #[css(dimension)]
    Dpi(CSSFloat),
    /// Dots per pixel.
    #[css(dimension)]
    Dppx(CSSFloat),
    /// Dots per centimeter.
    #[css(dimension)]
    Dpcm(CSSFloat),
}

impl Resolution {
    /// Convert this resolution value to dppx units.
    pub fn to_dppx(&self) -> CSSFloat {
        match *self {
            Resolution::Dppx(f) => f,
            _ => self.to_dpi() / 96.0,
        }
    }

    /// Convert this resolution value to dpi units.
    pub fn to_dpi(&self) -> CSSFloat {
        match *self {
            Resolution::Dpi(f) => f,
            Resolution::Dppx(f) => f * 96.0,
            Resolution::Dpcm(f) => f * 2.54,
        }
    }
}

impl Parse for Resolution {
    fn parse<'i, 't>(
        _: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let location = input.current_source_location();
        let (value, unit) = match *input.next()? {
            Token::Dimension {
                value, ref unit, ..
            } => (value, unit),
            ref t => return Err(location.new_unexpected_token_error(t.clone())),
        };

        if value <= 0. {
            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
        }

        match_ignore_ascii_case! { &unit,
            "dpi" => Ok(Resolution::Dpi(value)),
            "dppx" => Ok(Resolution::Dppx(value)),
            "dpcm" => Ok(Resolution::Dpcm(value)),
            _ => Err(location.new_custom_error(
                StyleParseErrorKind::UnexpectedDimension(unit.clone())
            )),
        }
    }
}
<commit_msg>style: Support x as a resolution unit.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Resolution values:
//!
//! https://drafts.csswg.org/css-values/#resolution

use cssparser::{Parser, Token};
use parser::{Parse, ParserContext};
use style_traits::{ParseError, StyleParseErrorKind};
use values::CSSFloat;

/// A specified resolution.
#[derive(Clone, Debug, PartialEq, ToCss)]
pub enum Resolution {
    /// Dots per inch.
    #[css(dimension)]
    Dpi(CSSFloat),
    /// An alias unit for dots per pixel.
    #[css(dimension)]
    X(CSSFloat),
    /// Dots per pixel.
    #[css(dimension)]
    Dppx(CSSFloat),
    /// Dots per centimeter.
    #[css(dimension)]
    Dpcm(CSSFloat),
}

impl Resolution {
    /// Convert this resolution value to dppx units.
    pub fn to_dppx(&self) -> CSSFloat {
        match *self {
            Resolution::X(f) |
            Resolution::Dppx(f) => f,
            _ => self.to_dpi() / 96.0,
        }
    }

    /// Convert this resolution value to dpi units.
    pub fn to_dpi(&self) -> CSSFloat {
        match *self {
            Resolution::Dpi(f) => f,
            Resolution::X(f) |
            Resolution::Dppx(f) => f * 96.0,
            Resolution::Dpcm(f) => f * 2.54,
        }
    }
}

impl Parse for Resolution {
    fn parse<'i, 't>(
        _: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let location = input.current_source_location();
        let (value, unit) = match *input.next()? {
            Token::Dimension {
                value, ref unit, ..
            } => (value, unit),
            ref t => return Err(location.new_unexpected_token_error(t.clone())),
        };

        if value <= 0. {
            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
        }

        match_ignore_ascii_case! { &unit,
            "dpi" => Ok(Resolution::Dpi(value)),
            "dppx" => Ok(Resolution::Dppx(value)),
            "dpcm" => Ok(Resolution::Dpcm(value)),
            "x" => Ok(Resolution::X(value)),
            _ => Err(location.new_custom_error(
                StyleParseErrorKind::UnexpectedDimension(unit.clone())
            )),
        }
    }
}
<|endoftext|>


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

#![comment = "The Servo Parallel Browser Project"]
#![license = "MPL"]

#![feature(phase)]

#![deny(unused_imports)]
#![deny(unused_variables)]

#[cfg(target_os="android")]
extern crate libc;

extern crate servo;
extern crate native;
extern crate time;
extern crate "util" as servo_util;

#[cfg(all(feature = "glutin",not(test)))]
extern crate "glutin_app" as app;
#[cfg(all(feature = "glfw_app",not(test)))]
extern crate "glfw_app" as app;

#[cfg(not(test))]
extern crate compositing;

#[cfg(target_os="android")]
#[phase(plugin, link)]
extern crate android_glue;

#[cfg(target_os="android")]
use libc::c_int;

#[cfg(not(test))]
use servo_util::opts;

#[cfg(not(test))]
use servo_util::rtinstrument;

#[cfg(not(test))]
use servo::Browser;
#[cfg(not(test))]
use compositing::windowing::WindowEvent;

#[cfg(not(any(test,target_os="android")))]
use std::os;

#[cfg(not(test))]
struct BrowserWrapper {
    browser: Browser<app::window::Window>,
}

#[cfg(target_os="android")]
android_start!(main)

#[cfg(target_os="android")]
fn get_args() -> Vec<String> {
    vec![
        "servo".to_string(),
        "http://en.wikipedia.org/wiki/Rust".to_string()
    ]
}

#[cfg(not(target_os="android"))]
fn get_args() -> Vec<String> {
    os::args()
}

#[cfg(target_os="android")]
fn redirect_output(file_no: c_int) {
    use libc::funcs::posix88::unistd::{pipe, dup2};
    use libc::funcs::posix88::stdio::fdopen;
    use libc::c_char;
    use libc::funcs::c95::stdio::fgets;
    use std::mem;
    use std::c_str::CString;

    unsafe {
        let mut pipes: [c_int, ..2] = [ 0, 0 ];
        pipe(pipes.as_mut_ptr());
        dup2(pipes[1], file_no);
        let input_file = "r".with_c_str(|mode| {
            fdopen(pipes[0], mode)
        });
        spawn(proc() {
            loop {
                let mut read_buffer: [c_char, ..1024] = mem::zeroed();
                fgets(read_buffer.as_mut_ptr(), read_buffer.len() as i32, input_file);
                let cs = CString::new(read_buffer.as_ptr(), false);
                match cs.as_str() {
                    Some(s) => android_glue::write_log(s),
                    None => {},
                }
            }
        });
    }
}

#[cfg(target_os="android")]
fn setup_logging() {
    use libc::consts::os::posix88::{STDERR_FILENO, STDOUT_FILENO};
    //os::setenv("RUST_LOG", "servo,gfx,msg,util,layers,js,std,rt,extra");
    redirect_output(STDERR_FILENO);
    redirect_output(STDOUT_FILENO);
}

#[cfg(not(target_os="android"))]
fn setup_logging() {
}

fn main() {
    if opts::from_cmdline_args(get_args().as_slice()) {
        setup_logging();

        let window = if opts::get().headless {
            None
        } else {
            Some(app::create_window())
        };

        let mut browser = BrowserWrapper {
            browser: Browser::new(window.clone()),
        };

        match window {
            None => {}
            Some(ref window) => {
                unsafe {
                    window.set_nested_event_loop_listener(&mut browser);
                }
            }
        }

        browser.browser.handle_event(WindowEvent::InitializeCompositing);

        loop {
            let should_continue = match window {
                None => browser.browser.handle_event(WindowEvent::Idle),
                Some(ref window) => {
                    let event = window.wait_events();
                    browser.browser.handle_event(event)
                }
            };
            if !should_continue {
                break
            }
        };

        match window {
            None => {}
            Some(ref window) => {
                unsafe {
                    window.remove_nested_event_loop_listener();
                }
            }
        }

        let BrowserWrapper {
            browser
        } = browser;
        browser.shutdown();

        rtinstrument::teardown();
    }
}

impl app::NestedEventLoopListener for BrowserWrapper {
    fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool {
        let is_resize = match event {
            WindowEvent::Resize(..) => true,
            _ => false,
        };
        if !self.browser.handle_event(event) {
            return false
        }
        if is_resize {
            self.browser.repaint_synchronously()
        }
        true
    }
}

<commit_msg>servo: to_string() -> into_string()<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![comment = "The Servo Parallel Browser Project"]
#![license = "MPL"]

#![feature(phase)]

#![deny(unused_imports)]
#![deny(unused_variables)]

#[cfg(target_os="android")]
extern crate libc;

extern crate servo;
extern crate native;
extern crate time;
extern crate "util" as servo_util;

#[cfg(all(feature = "glutin",not(test)))]
extern crate "glutin_app" as app;
#[cfg(all(feature = "glfw_app",not(test)))]
extern crate "glfw_app" as app;

#[cfg(not(test))]
extern crate compositing;

#[cfg(target_os="android")]
#[phase(plugin, link)]
extern crate android_glue;

#[cfg(target_os="android")]
use libc::c_int;

#[cfg(not(test))]
use servo_util::opts;

#[cfg(not(test))]
use servo_util::rtinstrument;

#[cfg(not(test))]
use servo::Browser;
#[cfg(not(test))]
use compositing::windowing::WindowEvent;

#[cfg(not(any(test,target_os="android")))]
use std::os;

#[cfg(not(test))]
struct BrowserWrapper {
    browser: Browser<app::window::Window>,
}

#[cfg(target_os="android")]
android_start!(main)

#[cfg(target_os="android")]
fn get_args() -> Vec<String> {
    vec![
        "servo".into_string(),
        "http://en.wikipedia.org/wiki/Rust".into_string()
    ]
}

#[cfg(not(target_os="android"))]
fn get_args() -> Vec<String> {
    os::args()
}

#[cfg(target_os="android")]
fn redirect_output(file_no: c_int) {
    use libc::funcs::posix88::unistd::{pipe, dup2};
    use libc::funcs::posix88::stdio::fdopen;
    use libc::c_char;
    use libc::funcs::c95::stdio::fgets;
    use std::mem;
    use std::c_str::CString;

    unsafe {
        let mut pipes: [c_int, ..2] = [ 0, 0 ];
        pipe(pipes.as_mut_ptr());
        dup2(pipes[1], file_no);
        let input_file = "r".with_c_str(|mode| {
            fdopen(pipes[0], mode)
        });
        spawn(proc() {
            loop {
                let mut read_buffer: [c_char, ..1024] = mem::zeroed();
                fgets(read_buffer.as_mut_ptr(), read_buffer.len() as i32, input_file);
                let cs = CString::new(read_buffer.as_ptr(), false);
                match cs.as_str() {
                    Some(s) => android_glue::write_log(s),
                    None => {},
                }
            }
        });
    }
}

#[cfg(target_os="android")]
fn setup_logging() {
    use libc::consts::os::posix88::{STDERR_FILENO, STDOUT_FILENO};
    //os::setenv("RUST_LOG", "servo,gfx,msg,util,layers,js,std,rt,extra");
    redirect_output(STDERR_FILENO);
    redirect_output(STDOUT_FILENO);
}

#[cfg(not(target_os="android"))]
fn setup_logging() {
}

fn main() {
    if opts::from_cmdline_args(get_args().as_slice()) {
        setup_logging();

        let window = if opts::get().headless {
            None
        } else {
            Some(app::create_window())
        };

        let mut browser = BrowserWrapper {
            browser: Browser::new(window.clone()),
        };

        match window {
            None => {}
            Some(ref window) => {
                unsafe {
                    window.set_nested_event_loop_listener(&mut browser);
                }
            }
        }

        browser.browser.handle_event(WindowEvent::InitializeCompositing);

        loop {
            let should_continue = match window {
                None => browser.browser.handle_event(WindowEvent::Idle),
                Some(ref window) => {
                    let event = window.wait_events();
                    browser.browser.handle_event(event)
                }
            };
            if !should_continue {
                break
            }
        };

        match window {
            None => {}
            Some(ref window) => {
                unsafe {
                    window.remove_nested_event_loop_listener();
                }
            }
        }

        let BrowserWrapper {
            browser
        } = browser;
        browser.shutdown();

        rtinstrument::teardown();
    }
}

impl app::NestedEventLoopListener for BrowserWrapper {
    fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool {
        let is_resize = match event {
            WindowEvent::Resize(..) => true,
            _ => false,
        };
        if !self.browser.handle_event(event) {
            return false
        }
        if is_resize {
            self.browser.repaint_synchronously()
        }
        true
    }
}

<|endoftext|>


<commit_before>use std::fmt;
use std::fs::File;
use std::io::Read;
use std::ptr;
use super::glium::{ DisplayBuild, Surface };
use super::glium::backend::glutin_backend::GlutinFacade;

const MEM_SIZE: usize = 4096;
const ROM_ADDR: usize = 0x200;

#[derive(Debug, Default)]
struct Registers {
    reg_gp: [u8; 16],
    reg_i: u16,

    reg_delay: u8,
    reg_sound: u8,

    reg_pc: u16,
    reg_sp: u8,

    stack: [u16; 16]
}

impl Registers {
    fn new() -> Registers {
        let mut reg = Registers::default();
        reg.reg_pc = (ROM_ADDR as u16);
        reg
    }

    fn write_register(&mut self, target_reg: u8, data_value: u8) {
        self.reg_gp[target_reg as usize] = data_value;
        println!("Loaded value {:#x} into register V{:x}", data_value, target_reg)
    }

    fn write_register_i(&mut self, data_value: u16) {
        self.reg_i = data_value;
        println!("Loaded value {:#x} into I register", data_value);
    }

    fn read_register(&self, target_reg: u8) -> u8 {
        self.reg_gp[target_reg as usize]
    }

    fn jump_to_address(&mut self, addr: u16, jump_type: JumpType) {
        match jump_type {
            JumpType::SUBROUTINE => {
                self.stack[self.reg_sp as usize] = self.reg_pc;
                self.reg_sp += 1;
            }
            JumpType::NORMAL => {}
        }
        println!("Jumping to address {:#x}", addr);
        self.reg_pc = addr;
    }

    fn return_from_subroutine(&mut self) {
        self.reg_pc = self.stack[self.reg_sp as usize];
        self.reg_sp -= 1;
    }
}

#[derive(Default, Debug)]
struct Keypad {
    keys: [u8; 16],
}

struct Memory {
    mem: [u8; MEM_SIZE],
}

impl Memory {
    fn store_program_data(&mut self, rom: File) {
        let mut last_stored_addr = ROM_ADDR;

        for byte in rom.bytes() {
            match byte {
                Ok(b) => {
                    self.mem[last_stored_addr] = b;
                    last_stored_addr += 1;
                },
                Err(e) => panic!("Some error {:?} occurred while storing program data.", e)
            }
        }
    }

    fn load_fonts(&mut self) {
        let font_file = File::open("./font.bin");
        let mem_addr = 0x0;
        for byte in font_file.bytes() {
            match byte {
                Ok(b) => {
                    self.mem[mem_addr] = b;
                    mem_addr += 1;
                },
                Err(e) => panic!("Some error {:?} occurred while loading font data.", e)
            }
        }
    }

    fn display_pong_rom(&self) {
        let mut addr = ROM_ADDR;
        for i in 1..100 {
            println!("{:#x}", self.mem[addr]);
            addr += 1;
        }
    }
}

impl fmt::Debug for Memory {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TODO implement debug output for memory")
    }
}

impl Default for Memory {
    fn default() -> Memory {
        Memory {
            mem: [0u8; MEM_SIZE]
        }
    }
}

pub struct Chip8 {
    reg: Registers,
    mem: Memory,
    keys: Keypad,
    display: GlutinFacade
}

impl fmt::Debug for Chip8 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#?}{:#?}{:#?}", self.reg, self.mem, self.keys)
    }
}

impl Chip8 {
    pub fn new() -> Chip8 {
        Chip8 {
            reg: Registers::new(),
            mem: Memory::default(),
            keys: Keypad::default(),
            display: super::glium::glutin::WindowBuilder::new()
                .with_dimensions(64, 32)
                .with_title(String::from("rust8"))
                .build_glium()
                .unwrap()
        }
    }

    pub fn init_display(&mut self) {
        let mut target = self.display.draw();
        target.clear_color(0.0, 0.0, 1.0, 1.0);
        target.finish().unwrap();

        self.mem.load_fonts();
    }

    pub fn run(&mut self) {
        loop {
            let instruction = self.read_word();
            self.process_instruction(instruction);

            for ev in self.display.poll_events() {
                match ev {
                    super::glium::glutin::Event::Closed => return,
                    _ => ()
                }
            }
        }
    }

    pub fn store_program_data(&mut self, rom: File) {
        self.mem.store_program_data(rom);
    }

    pub fn debug_pong_rom(&self) {
        self.mem.display_pong_rom();
    }

    fn read_word(&mut self) -> u16 {
        let instruction_high_order = (self.mem.mem[self.reg.reg_pc as usize] as u16) << 8;
        let instruction_low_order = self.mem.mem[(self.reg.reg_pc + 2) as usize] as u16;

        let instruction = instruction_high_order | instruction_low_order;
        println!("Instruction: {:#x}", instruction);

        self.reg.reg_pc += 4;
        instruction
    }

    fn process_instruction(&mut self, instruction: u16) {
        let op_type: u8 = ((instruction >> 12) & 0xff) as u8;

        match op_type {
            0x0 => {
                //we will ignore the 0nnn opcode used for jumping to machine code routines
                let operation = instruction & 0x00ff;
                if(operation == 0xe0) {
                    //TODO clear the display
                } else if (operation == 0xee) {
                    self.reg.return_from_subroutine();
                }
            },
            0x1 => {
                let jump_addr = instruction & 0x0fff;
                self.reg.jump_to_address(jump_addr, JumpType::NORMAL);
            },
            0x2 => {
                let subroutine_addr = instruction & 0x0fff;
                self.reg.jump_to_address(subroutine_addr, JumpType::SUBROUTINE);
            },
            0x6 => {
                let target_reg = ((instruction >> 8) & 0x0f) as u8;
                let data_value = (instruction & 0x00ff) as u8;
                self.reg.write_register(target_reg, data_value);
            },
            0x7 => {
                let target_reg = ((instruction >> 8) & 0x0f) as u8;
                let immediate_value = (instruction & 0x00ff) as u8;
                let reg_value = self.reg.read_register(target_reg);
                let data_value = immediate_value.wrapping_add(reg_value);
                println!("Addition");
                self.reg.write_register(target_reg, data_value);
            },
            0x8 => {
                let reg_one = ((instruction >> 8) & 0x0f) as u8;
                let reg_two = ((instruction >> 4) & 0x0f) as u8;
                let operation = (instruction & 0x000f) as u8;
                match operation {
                    0 => {
                        let data_value = self.reg.read_register(reg_two);
                        self.reg.write_register(reg_one, data_value);
                    },
                    1 => {
                        let reg_one_value = self.reg.read_register(reg_one);
                        let reg_two_value = self.reg.read_register(reg_two);
                        let data_value = reg_one_value | reg_two_value;
                        self.reg.write_register(reg_one, data_value);
                    },
                    2 => {
                        let reg_one_value = self.reg.read_register(reg_one);
                        let reg_two_value = self.reg.read_register(reg_two);
                        let data_value = reg_one_value & reg_two_value;
                        self.reg.write_register(reg_one, data_value);
                    },
                    3 => {
                        let reg_one_value = self.reg.read_register(reg_one);
                        let reg_two_value = self.reg.read_register(reg_two);
                        if(reg_two_value > reg_one_value) {
                            self.reg.write_register(0x0f, 0x01);
                        }
                        let data_value = reg_two_value - reg_one_value;
                        self.reg.write_register(reg_one, data_value);
                    },
                    4 => {

                    },
                    5 => {

                    },
                    6 => {

                    },
                    7 => {

                    },
                    0xe => {

                    },
                    _ => panic!("Unrecognized opcode: {:#x}", instruction)
                }
            },
            0xa => {
                let data_value = instruction & 0x0fff;
                self.reg.write_register_i(data_value);
            },
            0xb => {
                let initial_addr = instruction & 0x0fff;
                let offset = self.reg.read_register(0) as u16;
                self.reg.jump_to_address(initial_addr + offset, JumpType::NORMAL);
            },
            0xd => {
                //TODO: display/sprites
                println!("Display operation");
            },
            0xe => {
                //TODO: input checks
                println!("Input checks");
            },
            _ => {
                println!("Chip8 status at end time: {:#?}", self);
                panic!("Unsupported op type: {:#x}", op_type)
            }
        }
    }
}

enum JumpType {
    NORMAL, SUBROUTINE
}<commit_msg>start loading font sprites into memory, and looking at how to display them. need to implement a vertex struct and then use a vertexbuffer<commit_after>use std::fmt;
use std::fs::File;
use std::io::Read;
use std::ptr;
use super::glium::{ DisplayBuild, Surface };
use super::glium::backend::glutin_backend::GlutinFacade;

const MEM_SIZE: usize = 4096;
const ROM_ADDR: usize = 0x200;

#[derive(Debug, Default)]
struct Registers {
    reg_gp: [u8; 16],
    reg_i: u16,

    reg_delay: u8,
    reg_sound: u8,

    reg_pc: u16,
    reg_sp: u8,

    stack: [u16; 16]
}

impl Registers {
    fn new() -> Registers {
        let mut reg = Registers::default();
        reg.reg_pc = (ROM_ADDR as u16);
        reg
    }

    fn write_register(&mut self, target_reg: u8, data_value: u8) {
        self.reg_gp[target_reg as usize] = data_value;
        println!("Loaded value {:#x} into register V{:x}", data_value, target_reg)
    }

    fn write_register_i(&mut self, data_value: u16) {
        self.reg_i = data_value;
        println!("Loaded value {:#x} into I register", data_value);
    }

    fn read_register(&self, target_reg: u8) -> u8 {
        self.reg_gp[target_reg as usize]
    }

    fn jump_to_address(&mut self, addr: u16, jump_type: JumpType) {
        match jump_type {
            JumpType::SUBROUTINE => {
                self.stack[self.reg_sp as usize] = self.reg_pc;
                self.reg_sp += 1;
            }
            JumpType::NORMAL => {}
        }
        println!("Jumping to address {:#x}", addr);
        self.reg_pc = addr;
    }

    fn return_from_subroutine(&mut self) {
        self.reg_pc = self.stack[self.reg_sp as usize];
        self.reg_sp -= 1;
    }
}

#[derive(Default, Debug)]
struct Keypad {
    keys: [u8; 16],
}

struct Memory {
    mem: [u8; MEM_SIZE],
}

impl Memory {
    fn store_program_data(&mut self, rom: File) {
        let mut last_stored_addr = ROM_ADDR;

        for byte in rom.bytes() {
            match byte {
                Ok(b) => {
                    self.mem[last_stored_addr] = b;
                    last_stored_addr += 1;
                },
                Err(e) => panic!("Some error {:?} occurred while storing program data.", e)
            }
        }
    }

    fn load_fonts(&mut self) {
        let font_file = File::open("./font.bin").unwrap();
        let mut mem_addr = 0x0;
        for byte in font_file.bytes() {
            match byte {
                Ok(b) => {
                    self.mem[mem_addr] = b;
                    mem_addr += 1;
                },
                Err(e) => panic!("Some error {:?} occurred while loading font data.", e)
            }
        }
    }

    fn _display_pong_rom(&self) {
        let mut addr = ROM_ADDR;
        for _ in 1..100 {
            println!("{:#x}", self.mem[addr]);
            addr += 1;
        }
    }

    fn _display_font_data(&self) {
        let mut addr = 0x0;
        for _ in 0..80 {
            println!("{:#x}", self.mem[addr]);
            addr += 1;
        }
    }
}

impl fmt::Debug for Memory {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TODO implement mem debug")
    }
}

impl Default for Memory {
    fn default() -> Memory {
        Memory {
            mem: [0u8; MEM_SIZE]
        }
    }
}

pub struct Chip8 {
    reg: Registers,
    mem: Memory,
    keys: Keypad,
    display: GlutinFacade
}

impl fmt::Debug for Chip8 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#?}{:#?}{:#?}", self.reg, self.mem, self.keys)
    }
}

impl Chip8 {
    pub fn new() -> Chip8 {
        Chip8 {
            reg: Registers::new(),
            mem: Memory::default(),
            keys: Keypad::default(),
            display: super::glium::glutin::WindowBuilder::new()
                .with_dimensions(64, 32)
                .with_title(String::from("rust8"))
                .build_glium()
                .unwrap()
        }
    }

    pub fn init_display(&mut self) {
        let mut target = self.display.draw();
        target.clear_color(0.0, 0.0, 0.0, 1.0);
        target.finish().unwrap();

        self.mem.load_fonts();
    }

    pub fn run(&mut self) {
        loop {
            let instruction = self.read_word();
            self.process_instruction(instruction);

            for ev in self.display.poll_events() {
                match ev {
                    super::glium::glutin::Event::Closed => {
                        self._debug_font_data();
                        return;
                    },
                    _ => ()
                }
            }
        }
    }

    pub fn store_program_data(&mut self, rom: File) {
        self.mem.store_program_data(rom);
    }

    pub fn _debug_pong_rom(&self) {
        self.mem._display_pong_rom();
    }

    pub fn _debug_font_data(&self) {
        self.mem._display_font_data();
    }

    fn read_word(&mut self) -> u16 {
        let instruction_high_order = (self.mem.mem[self.reg.reg_pc as usize] as u16) << 8;
        let instruction_low_order = self.mem.mem[(self.reg.reg_pc + 2) as usize] as u16;

        let instruction = instruction_high_order | instruction_low_order;
        println!("Instruction: {:#x}", instruction);

        self.reg.reg_pc += 4;
        instruction
    }

    fn process_instruction(&mut self, instruction: u16) {
        let op_type: u8 = ((instruction >> 12) & 0xff) as u8;

        match op_type {
            0x0 => {
                //we will ignore the 0nnn opcode used for jumping to machine code routines
                let operation = instruction & 0x00ff;
                if(operation == 0xe0) {
                    let mut target = self.display.draw();
                    target.clear_color(0.0, 0.0, 0.0, 1.0);
                    target.finish().unwrap();
                } else if (operation == 0xee) {
                    self.reg.return_from_subroutine();
                }
            },
            0x1 => {
                let jump_addr = instruction & 0x0fff;
                self.reg.jump_to_address(jump_addr, JumpType::NORMAL);
            },
            0x2 => {
                let subroutine_addr = instruction & 0x0fff;
                self.reg.jump_to_address(subroutine_addr, JumpType::SUBROUTINE);
            },
            0x6 => {
                let target_reg = ((instruction >> 8) & 0x0f) as u8;
                let data_value = (instruction & 0x00ff) as u8;
                self.reg.write_register(target_reg, data_value);
            },
            0x7 => {
                let target_reg = ((instruction >> 8) & 0x0f) as u8;
                let immediate_value = (instruction & 0x00ff) as u8;
                let reg_value = self.reg.read_register(target_reg);
                let data_value = immediate_value.wrapping_add(reg_value);
                println!("Addition");
                self.reg.write_register(target_reg, data_value);
            },
            0x8 => {
                let reg_one = ((instruction >> 8) & 0x0f) as u8;
                let reg_two = ((instruction >> 4) & 0x0f) as u8;
                let operation = (instruction & 0x000f) as u8;
                match operation {
                    0 => {
                        let data_value = self.reg.read_register(reg_two);
                        self.reg.write_register(reg_one, data_value);
                    },
                    1 => {
                        let reg_one_value = self.reg.read_register(reg_one);
                        let reg_two_value = self.reg.read_register(reg_two);
                        let data_value = reg_one_value | reg_two_value;
                        self.reg.write_register(reg_one, data_value);
                    },
                    2 => {
                        let reg_one_value = self.reg.read_register(reg_one);
                        let reg_two_value = self.reg.read_register(reg_two);
                        let data_value = reg_one_value & reg_two_value;
                        self.reg.write_register(reg_one, data_value);
                    },
                    3 => {
                        let reg_one_value = self.reg.read_register(reg_one);
                        let reg_two_value = self.reg.read_register(reg_two);
                        if(reg_two_value > reg_one_value) {
                            self.reg.write_register(0x0f, 0x01);
                        }
                        let data_value = reg_two_value - reg_one_value;
                        self.reg.write_register(reg_one, data_value);
                    },
                    4 => {

                    },
                    5 => {

                    },
                    6 => {

                    },
                    7 => {

                    },
                    0xe => {

                    },
                    _ => panic!("Unrecognized opcode: {:#x}", instruction)
                }
            },
            0xa => {
                let data_value = instruction & 0x0fff;
                self.reg.write_register_i(data_value);
            },
            0xb => {
                let initial_addr = instruction & 0x0fff;
                let offset = self.reg.read_register(0) as u16;
                self.reg.jump_to_address(initial_addr + offset, JumpType::NORMAL);
            },
            0xd => {
                //num bytes is also the height of the sprite
                let num_bytes = (instruction & 0x000f) as u8;
                let x_pos = ((instruction >> 8) & 0x000f) as u8;
                let y_pos = ((instruction >> 4) & 0x000f) as u8;
                for 0..num_bytes {
                    //TODO implement vertice struct so we can make a vertex buffer and start displaying sprites
                }
                println!("Display operation");
            },
            0xe => {
                //TODO: input checks
                println!("Input checks");
            },
            _ => {
                println!("Chip8 status at end time: {:#?}", self);
                panic!("Unsupported op type: {:#x}", op_type)
            }
        }
    }
}

enum JumpType {
    NORMAL, SUBROUTINE
}<|endoftext|>