<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! mempool is used to track transactions which have been submitted but not yet
//! agreed upon.
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use crate::{
    core_mempool::{
        index::TxnPointer,
        transaction::{MempoolTransaction, TimelineState},
        transaction_store::TransactionStore,
    },
    OP_COUNTERS,
};
use chrono::Utc;
use libra_config::config::NodeConfig;
use libra_logger::prelude::*;
use libra_mempool_shared_proto::{
    proto::mempool_status::MempoolAddTransactionStatusCode, MempoolAddTransactionStatus,
};
use libra_types::{account_address::AccountAddress, transaction::SignedTransaction};
use lru_cache::LruCache;
use std::{cmp::max, collections::HashSet, convert::TryFrom};
use ttl_cache::TtlCache;

pub struct Mempool {
    // stores metadata of all transactions in mempool (of all states)
    transactions: TransactionStore,

    sequence_number_cache: LruCache<AccountAddress, u64>,
    // temporary DS. TODO: eventually retire it
    // for each transaction, entry with timestamp is added when transaction enters mempool
    // used to measure e2e latency of transaction in system, as well as time it takes to pick it up
    // by consensus
    pub(crate) metrics_cache: TtlCache<(AccountAddress, u64), i64>,
    pub system_transaction_timeout: Duration,
}

impl Mempool {
    pub fn new(config: &NodeConfig) -> Self {
        Mempool {
            transactions: TransactionStore::new(&config.mempool),
            sequence_number_cache: LruCache::new(config.mempool.capacity),
            metrics_cache: TtlCache::new(config.mempool.capacity),
            system_transaction_timeout: Duration::from_secs(
                config.mempool.system_transaction_timeout_secs,
            ),
        }
    }

    /// This function will be called once the transaction has been stored
    pub(crate) fn remove_transaction(
        &mut self,
        sender: &AccountAddress,
        sequence_number: u64,
        is_rejected: bool,
    ) {
        debug!(
            "[Mempool] Removing transaction from mempool: {}:{}:{}",
            sender, sequence_number, is_rejected
        );
        self.log_latency(sender.clone(), sequence_number, "e2e.latency");
        self.metrics_cache.remove(&(*sender, sequence_number));
        OP_COUNTERS.inc(&format!("remove_transaction.{}", is_rejected));

        let current_seq_number = self
            .sequence_number_cache
            .remove(&sender)
            .unwrap_or_default();

        if is_rejected {
            debug!(
                "[Mempool] transaction is rejected: {}:{}",
                sender, sequence_number
            );
            if sequence_number >= current_seq_number {
                self.transactions
                    .reject_transaction(&sender, sequence_number);
            }
        } else {
            // update current cached sequence number for account
            let new_seq_number = max(current_seq_number, sequence_number + 1);
            self.sequence_number_cache
                .insert(sender.clone(), new_seq_number);
            self.transactions
                .commit_transaction(&sender, new_seq_number);
        }
    }

    fn log_latency(&mut self, account: AccountAddress, sequence_number: u64, metric: &str) {
        if let Some(&creation_time) = self.metrics_cache.get(&(account, sequence_number)) {
            if let Ok(time_delta_ms) = u64::try_from(Utc::now().timestamp_millis() - creation_time)
            {
                OP_COUNTERS.observe_duration(metric, Duration::from_millis(time_delta_ms));
            }
        }
    }

    fn get_required_balance(&mut self, txn: &SignedTransaction, gas_amount: u64) -> u128 {
        txn.gas_unit_price() as u128 * gas_amount as u128
            + self.transactions.get_required_balance(&txn.sender()) as u128
    }

    /// Used to add a transaction to the Mempool
    /// Performs basic validation: checks account's balance and sequence number
    pub(crate) fn add_txn(
        &mut self,
        txn: SignedTransaction,
        gas_amount: u64,
        db_sequence_number: u64,
        balance: u64,
        timeline_state: TimelineState,
    ) -> MempoolAddTransactionStatus {
        debug!(
            "[Mempool] Adding transaction to mempool: {}:{}:{}",
            &txn.sender(),
            txn.sequence_number(),
            db_sequence_number,
        );

        let required_balance = self.get_required_balance(&txn, gas_amount);
        if (balance as u128) < required_balance {
            return MempoolAddTransactionStatus::new(
                MempoolAddTransactionStatusCode::InsufficientBalance,
                format!(
                    "balance: {}, required_balance: {}, gas_amount: {}",
                    balance, required_balance, gas_amount
                ),
            );
        }

        let cached_value = self.sequence_number_cache.get_mut(&txn.sender());
        let sequence_number =
            cached_value.map_or(db_sequence_number, |value| max(*value, db_sequence_number));
        self.sequence_number_cache
            .insert(txn.sender(), sequence_number);

        // don't accept old transactions (e.g. seq is less than account's current seq_number)
        if txn.sequence_number() < sequence_number {
            return MempoolAddTransactionStatus::new(
                MempoolAddTransactionStatusCode::InvalidSeqNumber,
                format!(
                    "transaction sequence number is {}, current sequence number is  {}",
                    txn.sequence_number(),
                    sequence_number,
                ),
            );
        }

        let expiration_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("init timestamp failure")
            + self.system_transaction_timeout;
        if timeline_state != TimelineState::NonQualified {
            self.metrics_cache.insert(
                (txn.sender(), txn.sequence_number()),
                Utc::now().timestamp_millis(),
                Duration::from_secs(100),
            );
        }

        let txn_info = MempoolTransaction::new(txn, expiration_time, gas_amount, timeline_state);

        let status = self.transactions.insert(txn_info, sequence_number);
        OP_COUNTERS.inc(&format!("insert.{:?}", status));
        status
    }

    /// Fetches next block of transactions for consensus
    /// `batch_size` - size of requested block
    /// `seen_txns` - transactions that were sent to Consensus but were not committed yet
    ///  Mempool should filter out such transactions
    pub(crate) fn get_block(
        &mut self,
        batch_size: u64,
        mut seen: HashSet<TxnPointer>,
    ) -> Vec<SignedTransaction> {
        let mut result = vec![];
        // Helper DS. Helps to mitigate scenarios where account submits several transactions
        // with increasing gas price (e.g. user submits transactions with sequence number 1, 2
        // and gas_price 1, 10 respectively)
        // Later txn has higher gas price and will be observed first in priority index iterator,
        // but can't be executed before first txn. Once observed, such txn will be saved in
        // `skipped` DS and rechecked once it's ancestor becomes available
        let mut skipped = HashSet::new();

        // iterate over the queue of transactions based on gas price
        'main: for txn in self.transactions.iter_queue() {
            if seen.contains(&TxnPointer::from(txn)) {
                continue;
            }
            let mut seq = txn.sequence_number;
            let account_sequence_number = self.sequence_number_cache.get_mut(&txn.address);
            let seen_previous = seq > 0 && seen.contains(&(txn.address, seq - 1));
            // include transaction if it's "next" for given account or
            // we've already sent its ancestor to Consensus
            if seen_previous || account_sequence_number == Some(&mut seq) {
                let ptr = TxnPointer::from(txn);
                seen.insert(ptr);
                result.push(ptr);
                if (result.len() as u64) == batch_size {
                    break;
                }

                // check if we can now include some transactions
                // that were skipped before for given account
                let mut skipped_txn = (txn.address, seq + 1);
                while skipped.contains(&skipped_txn) {
                    seen.insert(skipped_txn);
                    result.push(skipped_txn);
                    if (result.len() as u64) == batch_size {
                        break 'main;
                    }
                    skipped_txn = (txn.address, skipped_txn.1 + 1);
                }
            } else {
                skipped.insert(TxnPointer::from(txn));
            }
        }
        // convert transaction pointers to real values
        let block: Vec<_> = result
            .into_iter()
            .filter_map(|(address, seq)| self.transactions.get(&address, seq))
            .collect();
        for transaction in &block {
            self.log_latency(
                transaction.sender(),
                transaction.sequence_number(),
                "txn_pre_consensus_s",
            );
        }
        block
    }

    /// TTL based garbage collection. Remove all transactions that got expired
    pub(crate) fn gc_by_system_ttl(&mut self) {
        self.transactions.gc_by_system_ttl();
    }

    /// Garbage collection based on client-specified expiration time
    pub(crate) fn gc_by_expiration_time(&mut self, block_time: Duration) {
        self.transactions.gc_by_expiration_time(block_time);
    }

    /// Read `count` transactions from timeline since `timeline_id`
    /// Returns block of transactions and new last_timeline_id
    pub(crate) fn read_timeline(
        &mut self,
        timeline_id: u64,
        count: usize,
    ) -> (Vec<SignedTransaction>, u64) {
        self.transactions.read_timeline(timeline_id, count)
    }
}
<commit_msg>[mempool] Log number of transactions returned to consensus<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! mempool is used to track transactions which have been submitted but not yet
//! agreed upon.
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use crate::{
    core_mempool::{
        index::TxnPointer,
        transaction::{MempoolTransaction, TimelineState},
        transaction_store::TransactionStore,
    },
    OP_COUNTERS,
};
use chrono::Utc;
use libra_config::config::NodeConfig;
use libra_logger::prelude::*;
use libra_mempool_shared_proto::{
    proto::mempool_status::MempoolAddTransactionStatusCode, MempoolAddTransactionStatus,
};
use libra_types::{account_address::AccountAddress, transaction::SignedTransaction};
use lru_cache::LruCache;
use std::{cmp::max, collections::HashSet, convert::TryFrom};
use ttl_cache::TtlCache;

pub struct Mempool {
    // stores metadata of all transactions in mempool (of all states)
    transactions: TransactionStore,

    sequence_number_cache: LruCache<AccountAddress, u64>,
    // temporary DS. TODO: eventually retire it
    // for each transaction, entry with timestamp is added when transaction enters mempool
    // used to measure e2e latency of transaction in system, as well as time it takes to pick it up
    // by consensus
    pub(crate) metrics_cache: TtlCache<(AccountAddress, u64), i64>,
    pub system_transaction_timeout: Duration,
}

impl Mempool {
    pub fn new(config: &NodeConfig) -> Self {
        Mempool {
            transactions: TransactionStore::new(&config.mempool),
            sequence_number_cache: LruCache::new(config.mempool.capacity),
            metrics_cache: TtlCache::new(config.mempool.capacity),
            system_transaction_timeout: Duration::from_secs(
                config.mempool.system_transaction_timeout_secs,
            ),
        }
    }

    /// This function will be called once the transaction has been stored
    pub(crate) fn remove_transaction(
        &mut self,
        sender: &AccountAddress,
        sequence_number: u64,
        is_rejected: bool,
    ) {
        debug!(
            "[Mempool] Removing transaction from mempool: {}:{}:{}",
            sender, sequence_number, is_rejected
        );
        self.log_latency(sender.clone(), sequence_number, "e2e.latency");
        self.metrics_cache.remove(&(*sender, sequence_number));
        OP_COUNTERS.inc(&format!("remove_transaction.{}", is_rejected));

        let current_seq_number = self
            .sequence_number_cache
            .remove(&sender)
            .unwrap_or_default();

        if is_rejected {
            debug!(
                "[Mempool] transaction is rejected: {}:{}",
                sender, sequence_number
            );
            if sequence_number >= current_seq_number {
                self.transactions
                    .reject_transaction(&sender, sequence_number);
            }
        } else {
            // update current cached sequence number for account
            let new_seq_number = max(current_seq_number, sequence_number + 1);
            self.sequence_number_cache
                .insert(sender.clone(), new_seq_number);
            self.transactions
                .commit_transaction(&sender, new_seq_number);
        }
    }

    fn log_latency(&mut self, account: AccountAddress, sequence_number: u64, metric: &str) {
        if let Some(&creation_time) = self.metrics_cache.get(&(account, sequence_number)) {
            if let Ok(time_delta_ms) = u64::try_from(Utc::now().timestamp_millis() - creation_time)
            {
                OP_COUNTERS.observe_duration(metric, Duration::from_millis(time_delta_ms));
            }
        }
    }

    fn get_required_balance(&mut self, txn: &SignedTransaction, gas_amount: u64) -> u128 {
        txn.gas_unit_price() as u128 * gas_amount as u128
            + self.transactions.get_required_balance(&txn.sender()) as u128
    }

    /// Used to add a transaction to the Mempool
    /// Performs basic validation: checks account's balance and sequence number
    pub(crate) fn add_txn(
        &mut self,
        txn: SignedTransaction,
        gas_amount: u64,
        db_sequence_number: u64,
        balance: u64,
        timeline_state: TimelineState,
    ) -> MempoolAddTransactionStatus {
        debug!(
            "[Mempool] Adding transaction to mempool: {}:{}:{}",
            &txn.sender(),
            txn.sequence_number(),
            db_sequence_number,
        );

        let required_balance = self.get_required_balance(&txn, gas_amount);
        if (balance as u128) < required_balance {
            return MempoolAddTransactionStatus::new(
                MempoolAddTransactionStatusCode::InsufficientBalance,
                format!(
                    "balance: {}, required_balance: {}, gas_amount: {}",
                    balance, required_balance, gas_amount
                ),
            );
        }

        let cached_value = self.sequence_number_cache.get_mut(&txn.sender());
        let sequence_number =
            cached_value.map_or(db_sequence_number, |value| max(*value, db_sequence_number));
        self.sequence_number_cache
            .insert(txn.sender(), sequence_number);

        // don't accept old transactions (e.g. seq is less than account's current seq_number)
        if txn.sequence_number() < sequence_number {
            return MempoolAddTransactionStatus::new(
                MempoolAddTransactionStatusCode::InvalidSeqNumber,
                format!(
                    "transaction sequence number is {}, current sequence number is  {}",
                    txn.sequence_number(),
                    sequence_number,
                ),
            );
        }

        let expiration_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("init timestamp failure")
            + self.system_transaction_timeout;
        if timeline_state != TimelineState::NonQualified {
            self.metrics_cache.insert(
                (txn.sender(), txn.sequence_number()),
                Utc::now().timestamp_millis(),
                Duration::from_secs(100),
            );
        }

        let txn_info = MempoolTransaction::new(txn, expiration_time, gas_amount, timeline_state);

        let status = self.transactions.insert(txn_info, sequence_number);
        OP_COUNTERS.inc(&format!("insert.{:?}", status));
        status
    }

    /// Fetches next block of transactions for consensus
    /// `batch_size` - size of requested block
    /// `seen_txns` - transactions that were sent to Consensus but were not committed yet
    ///  Mempool should filter out such transactions
    #[allow(clippy::explicit_counter_loop)]
    pub(crate) fn get_block(
        &mut self,
        batch_size: u64,
        mut seen: HashSet<TxnPointer>,
    ) -> Vec<SignedTransaction> {
        let mut result = vec![];
        // Helper DS. Helps to mitigate scenarios where account submits several transactions
        // with increasing gas price (e.g. user submits transactions with sequence number 1, 2
        // and gas_price 1, 10 respectively)
        // Later txn has higher gas price and will be observed first in priority index iterator,
        // but can't be executed before first txn. Once observed, such txn will be saved in
        // `skipped` DS and rechecked once it's ancestor becomes available
        let mut skipped = HashSet::new();
        let seen_size = seen.len();
        let mut txn_walked = 0usize;
        // iterate over the queue of transactions based on gas price
        'main: for txn in self.transactions.iter_queue() {
            txn_walked += 1;
            if seen.contains(&TxnPointer::from(txn)) {
                continue;
            }
            let mut seq = txn.sequence_number;
            let account_sequence_number = self.sequence_number_cache.get_mut(&txn.address);
            let seen_previous = seq > 0 && seen.contains(&(txn.address, seq - 1));
            // include transaction if it's "next" for given account or
            // we've already sent its ancestor to Consensus
            if seen_previous || account_sequence_number == Some(&mut seq) {
                let ptr = TxnPointer::from(txn);
                seen.insert(ptr);
                result.push(ptr);
                if (result.len() as u64) == batch_size {
                    break;
                }

                // check if we can now include some transactions
                // that were skipped before for given account
                let mut skipped_txn = (txn.address, seq + 1);
                while skipped.contains(&skipped_txn) {
                    seen.insert(skipped_txn);
                    result.push(skipped_txn);
                    if (result.len() as u64) == batch_size {
                        break 'main;
                    }
                    skipped_txn = (txn.address, skipped_txn.1 + 1);
                }
            } else {
                skipped.insert(TxnPointer::from(txn));
            }
        }
        let result_size = result.len();
        // convert transaction pointers to real values
        let block: Vec<_> = result
            .into_iter()
            .filter_map(|(address, seq)| self.transactions.get(&address, seq))
            .collect();
        debug!("mempool::get_block: seen_consensus={}, walked={}, seen_after={}, result_size={}, block_size={}",
               seen_size, txn_walked, seen.len(), result_size, block.len());
        for transaction in &block {
            self.log_latency(
                transaction.sender(),
                transaction.sequence_number(),
                "txn_pre_consensus_s",
            );
        }
        block
    }

    /// TTL based garbage collection. Remove all transactions that got expired
    pub(crate) fn gc_by_system_ttl(&mut self) {
        self.transactions.gc_by_system_ttl();
    }

    /// Garbage collection based on client-specified expiration time
    pub(crate) fn gc_by_expiration_time(&mut self, block_time: Duration) {
        self.transactions.gc_by_expiration_time(block_time);
    }

    /// Read `count` transactions from timeline since `timeline_id`
    /// Returns block of transactions and new last_timeline_id
    pub(crate) fn read_timeline(
        &mut self,
        timeline_id: u64,
        count: usize,
    ) -> (Vec<SignedTransaction>, u64) {
        self.transactions.read_timeline(timeline_id, count)
    }
}
<|endoftext|>


<commit_before>extern crate xml;

pub use xml::common::TextPosition;
pub use xml::reader::Error as XmlError;

use std::io::Read;
// use std::str::FromStr;
use xml::common::Position;
use xml::EventReader;
use xml::reader::XmlEvent;
use xml::reader::XmlEvent::*;

pub static COLLADA_ATTRIBS: &'static [&'static str] = &["version", "xmlns", "base"];

/// Represents a parsed COLLADA document.
#[derive(Debug, Clone)]
pub struct Collada {
    /// The version string for the COLLADA specification used by the document.
    ///
    /// Only "1.4.0", "1.4.1", and "1.5.0" are supported currently.
    pub version: String,

    /// The base uri for any relative URIs in the document.
    ///
    /// Specified by the `base` attribute on the root `<COLLADA>` element.
    base_uri: Option<AnyUri>,
}

impl Collada {
    /// Read a COLLADA document from a string.
    pub fn from_str(source: &str) -> Result<Collada> {
        let reader = EventReader::from_str(source);
        Collada::parse(reader)
    }

    /// Attempts to parse the contents of a COLLADA document.
    pub fn read<R: Read>(reader: R) -> Result<Collada> {
        let reader = EventReader::new(reader);
        Collada::parse(reader)
    }

    fn parse<R: Read>(mut reader: EventReader<R>) -> Result<Collada> {
        let mut collada = Collada {
            version: String::new(),
            base_uri: None,
        };

        // Eat the `StartDocument` event. It has no useful information for our purposes, but it
        // will always be the first event emitted.
        match reader.next()? {
            StartDocument { .. } => {},
            _ => panic!("First event from EventReader wasn't StartDocument"),
        }

        // The next element will always be the `<COLLADA>` tag. This will specify what version of
        // the COLLADA spec is being used, which is how we'll determine our sub-parser.
        match reader.next()? {
            StartElement { name, attributes, namespace: _ } => {
                // If the element isn't the `<COLLADA>` tag then the document is malformed,
                // return an error.
                if name.local_name != "COLLADA" {
                    return Err(Error {
                        position: reader.position(),
                        kind: ErrorKind::UnexpectedRootElement {
                            element: name.local_name,
                        }
                    })
                }

                // Valide the attributes on the `<COLLADA>` tag.
                // Use boolean flags to track if specific attributes have been encountered.
                let mut has_found_version = false;
                let mut has_found_base = false;

                for attribute in attributes {
                    // NOTE: I'm using `if` blocks instead of `match` here because using `match`
                    // won't allow for the name to be moved out of `attribute`. Using `if` saves
                    // some unnecessary allocations. I expect at some point Rust will get smart
                    // enough that this will no longer be an issue, at which point we should
                    // change this to use `match`, as that keeps better with Rust best practices.
                    if attribute.name.local_name == "version" {
                        if !has_found_version {
                            collada.version = attribute.value;
                            has_found_version = true;
                        } else {
                            return Err(Error {
                                position: reader.position(),
                                kind: ErrorKind::UnexpectedAttribute {
                                    element: name.local_name,
                                    attribute: "version".to_owned(),
                                    expected: COLLADA_ATTRIBS,
                                    is_duplicate: true,
                                },
                            })
                        }
                    } else if attribute.name.local_name == "base" {
                        if !has_found_base {
                            // TODO: Do we need to validate the URI?
                            collada.base_uri = Some(AnyUri(attribute.value));
                            has_found_base = true;
                        } else {
                            return Err(Error {
                                position: reader.position(),
                                kind: ErrorKind::UnexpectedAttribute {
                                    element: name.local_name,
                                    attribute: "base".to_owned(),
                                    expected: COLLADA_ATTRIBS,
                                    is_duplicate: true,
                                },
                            })
                        }
                    } else {
                        return Err(Error {
                            position: reader.position(),
                            kind: ErrorKind::UnexpectedAttribute {
                                element: name.local_name,
                                attribute: attribute.name.local_name,
                                expected: COLLADA_ATTRIBS,
                                is_duplicate: false,
                            },
                        })
                    }
                }

                // Verify that all required attributes have been found.
                if !has_found_version {
                    return Err(Error {
                        position: reader.position(),
                        kind: ErrorKind::MissingAttribute {
                            element: name.local_name,
                            attribute: "version",
                        },
                    })
                }
            }

            // Any other event is an error.
            event @ _ => return Err(Error {
                position: reader.position(),
                kind: ErrorKind::UnexpectedEvent(event),
            }),
        }

        // TODO: Eat any events until we get to the `</COLLADA>` tag.
        // TODO: Verify the next event is the `EndDocument` event.

        Ok(collada)
    }
}

/// A COLLADA parsing error.
///
/// Contains where in the document the error occurred (i.e. line number and column), and
/// details about the nature of the error.
// TODO: Implement Display for Error.
#[derive(Debug)]
pub struct Error {
    position: TextPosition,
    kind: ErrorKind,
}

impl Error {
    /// Gets the position in the document where the error occurred.
    pub fn position(&self) -> TextPosition { self.position }

    /// Gets detailed information about the error.
    pub fn kind(&self) -> &ErrorKind { &self.kind }
}

/// The specific error variant.
#[derive(Debug, PartialEq)]
pub enum ErrorKind {
    /// An element was missing a required attribute.
    MissingAttribute {
        /// The element that was missing an attribute.
        element: String,

        /// The attribute that expected to be present.
        attribute: &'static str,
    },

    /// An element had an attribute that isn't allowed.
    UnexpectedAttribute {
        /// The element that had the unexpected attribute.
        element: String,

        /// The unexpected attribute.
        attribute: String,

        /// The set of attributes allowed for this element.
        expected: &'static [&'static str],

        /// Whether the attribute appeared multiple times.
        is_duplicate: bool,
    },

    /// An element had a child element that wasn't allowed.
    UnexpectedElement {
        parent: String,
        element: String,
        expected: &'static [&'static str],
    },

    /// The document started with an element other than `<COLLADA>`.
    UnexpectedRootElement {
        element: String,
    },

    /// Generic error covering any case where less specific information is available.
    UnexpectedEvent(XmlEvent),

    /// The XML in the document was malformed in some way.
    XmlError(XmlError),
}

impl From<xml::reader::Error> for Error {
    fn from(from: xml::reader::Error) -> Error {
        Error {
            position: from.position(),
            kind: ErrorKind::XmlError(from),
        }
    }
}

pub type Result<T> = std::result::Result<T, Error>;

/// A URI in the COLLADA document.
///
/// Represents the [`xs:anyURI`][anyURI] XML data type.
///
/// [anyURI]: http://www.datypic.com/sc/xsd/t-xsd_anyURI.html
#[derive(Debug, Clone)]
pub struct AnyUri(String);
<commit_msg>Add crate-level documentation<commit_after>//! A library for parsing and processing COLLADA documents.
//!
//! [COLLADA][COLLADA] is a COLLAborative Design Activity that defines an XML-based schema to
//! enable 3D authoring applications to freely exchange digital assets. It supports a vast array of
//! features used in 3D modeling, animation, and VFX work, and provides and open, non-proprietary
//! alternative to common formats like [FBX][FBX].
//!
//! This provides functionality for parsing a COLLADA document and utilities for processing the
//! contained data, with the intention of enable direct usage of COLLADA data as well as
//! interchange of document data into other formats.
//!
//! # Quick Start
//!
//! The easiest way to parse a COLLADA document is to load it from a file and use
//! [`Collada::read()`][Collada::read]:
//!
//! ```
//! # #![allow(unused_variables)]
//! use std::fs::File;
//! use parse_collada::Collada;
//!
//! let file = File::open("resources/blender_cube.dae").unwrap();
//! let collada = Collada::read(file).unwrap();
//! ```
//!
//! The resulting [`Collada`][Collada] object provides direct access to all data in the document,
//! directly recreating the logical structure of the document as a Rust type.
//!
//! # COLLADA Versions
//!
//! Currently there are 3 COLLADA versions supported by this library: `1.4.0`, `1.4.1`, and
//! `1.5.0`. Older versions are not supported, but may be added if there is reason to do so. This
//! library attempts to normalize data across versions by "upgrading" all documents to match the
//! `1.5.0` specification. This removes the need for client code to be aware of the specification
//! version used by documents it handles. This conversion is done transparently without the need
//! for user specification.
//!
//! # 3rd Party Extensions
//!
//! The COLLADA format allows for semi-arbitrary extensions to the standard, allowing applications
//! to include application-specific data. This extra data is considered "optional", but may allow
//! applications consuming the COLLADA document to more accurately recreate the scene contained
//! in the document. This library attempts to directly support common 3rd party extensions,
//! primarily those for Blender and Maya. In the case that the 3rd party extension is not
//! directly supported, the underlying XML will be preserved so that the client code can attempt
//! to still use the data.
//!
//! [COLLADA]: https://www.khronos.org/collada/
//! [FBX]: https://en.wikipedia.org/wiki/FBX
//! [Collada]: struct.Collada.html
//! [Collada::read]: struct.Collada.html#method.read

extern crate xml;

pub use xml::common::TextPosition;
pub use xml::reader::Error as XmlError;

use std::io::Read;
use xml::common::Position;
use xml::EventReader;
use xml::reader::XmlEvent;
use xml::reader::XmlEvent::*;

pub static COLLADA_ATTRIBS: &'static [&'static str] = &["version", "xmlns", "base"];

/// Represents a parsed COLLADA document.
#[derive(Debug, Clone)]
pub struct Collada {
    /// The version string for the COLLADA specification used by the document.
    ///
    /// Only "1.4.0", "1.4.1", and "1.5.0" are supported currently.
    pub version: String,

    /// The base uri for any relative URIs in the document.
    ///
    /// Specified by the `base` attribute on the root `<COLLADA>` element.
    base_uri: Option<AnyUri>,
}

impl Collada {
    /// Read a COLLADA document from a string.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![allow(unused_variables)]
    /// use parse_collada::Collada;
    ///
    /// static DOCUMENT: &'static str = r#"
    ///     <?xml version="1.0" encoding="utf-8"?>
    ///     <COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
    ///     </COLLADA>
    /// "#;
    ///
    /// let collada = Collada::from_str(DOCUMENT).unwrap();
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `Err` if the document is invalid or malformed in some way. For details about
    /// COLLADA versions, 3rd party extensions, and any other details that could influence how
    /// a document is parsed see the [crate-level documentation][crate].
    ///
    /// [crate]: index.html
    pub fn from_str(source: &str) -> Result<Collada> {
        let reader = EventReader::from_str(source);
        Collada::parse(reader)
    }

    /// Attempts to parse the contents of a COLLADA document.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![allow(unused_variables)]
    /// use std::fs::File;
    /// use parse_collada::Collada;
    ///
    /// let file = File::open("resources/blender_cube.dae").unwrap();
    /// let collada = Collada::read(file).unwrap();
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `Err` if the document is invalid or malformed in some way. For details about
    /// COLLADA versions, 3rd party extensions, and any other details that could influence how
    /// a document is parsed see the [crate-level documentation][crate].
    ///
    /// [crate]: index.html
    pub fn read<R: Read>(reader: R) -> Result<Collada> {
        let reader = EventReader::new(reader);
        Collada::parse(reader)
    }

    /// The logic behind parsing the COLLADA document.
    ///
    /// `from_str()` and `read()` just create the `xml::EventReader` and then defer to `parse()`.
    fn parse<R: Read>(mut reader: EventReader<R>) -> Result<Collada> {
        let mut collada = Collada {
            version: String::new(),
            base_uri: None,
        };

        // Eat the `StartDocument` event. It has no useful information for our purposes, but it
        // will always be the first event emitted.
        match reader.next()? {
            StartDocument { .. } => {},
            _ => panic!("First event from EventReader wasn't StartDocument"),
        }

        // The next element will always be the `<COLLADA>` tag. This will specify what version of
        // the COLLADA spec is being used, which is how we'll determine our sub-parser.
        match reader.next()? {
            StartElement { name, attributes, namespace: _ } => {
                // If the element isn't the `<COLLADA>` tag then the document is malformed,
                // return an error.
                if name.local_name != "COLLADA" {
                    return Err(Error {
                        position: reader.position(),
                        kind: ErrorKind::UnexpectedRootElement {
                            element: name.local_name,
                        }
                    })
                }

                // Valide the attributes on the `<COLLADA>` tag.
                // Use boolean flags to track if specific attributes have been encountered.
                let mut has_found_version = false;
                let mut has_found_base = false;

                for attribute in attributes {
                    // NOTE: I'm using `if` blocks instead of `match` here because using `match`
                    // won't allow for the name to be moved out of `attribute`. Using `if` saves
                    // some unnecessary allocations. I expect at some point Rust will get smart
                    // enough that this will no longer be an issue, at which point we should
                    // change this to use `match`, as that keeps better with Rust best practices.
                    if attribute.name.local_name == "version" {
                        if !has_found_version {
                            collada.version = attribute.value;
                            has_found_version = true;
                        } else {
                            return Err(Error {
                                position: reader.position(),
                                kind: ErrorKind::UnexpectedAttribute {
                                    element: name.local_name,
                                    attribute: "version".to_owned(),
                                    expected: COLLADA_ATTRIBS,
                                    is_duplicate: true,
                                },
                            })
                        }
                    } else if attribute.name.local_name == "base" {
                        if !has_found_base {
                            // TODO: Do we need to validate the URI?
                            collada.base_uri = Some(AnyUri(attribute.value));
                            has_found_base = true;
                        } else {
                            return Err(Error {
                                position: reader.position(),
                                kind: ErrorKind::UnexpectedAttribute {
                                    element: name.local_name,
                                    attribute: "base".to_owned(),
                                    expected: COLLADA_ATTRIBS,
                                    is_duplicate: true,
                                },
                            })
                        }
                    } else {
                        return Err(Error {
                            position: reader.position(),
                            kind: ErrorKind::UnexpectedAttribute {
                                element: name.local_name,
                                attribute: attribute.name.local_name,
                                expected: COLLADA_ATTRIBS,
                                is_duplicate: false,
                            },
                        })
                    }
                }

                // Verify that all required attributes have been found.
                if !has_found_version {
                    return Err(Error {
                        position: reader.position(),
                        kind: ErrorKind::MissingAttribute {
                            element: name.local_name,
                            attribute: "version",
                        },
                    })
                }
            }

            // Any other event is an error.
            event @ _ => return Err(Error {
                position: reader.position(),
                kind: ErrorKind::UnexpectedEvent(event),
            }),
        }

        // TODO: Eat any events until we get to the `</COLLADA>` tag.
        // TODO: Verify the next event is the `EndDocument` event.

        Ok(collada)
    }
}

/// A COLLADA parsing error.
///
/// Contains where in the document the error occurred (i.e. line number and column), and
/// details about the nature of the error.
// TODO: Implement Display for Error.
#[derive(Debug)]
pub struct Error {
    position: TextPosition,
    kind: ErrorKind,
}

impl Error {
    /// Gets the position in the document where the error occurred.
    pub fn position(&self) -> TextPosition { self.position }

    /// Gets detailed information about the error.
    pub fn kind(&self) -> &ErrorKind { &self.kind }
}

/// The specific error variant.
#[derive(Debug, PartialEq)]
pub enum ErrorKind {
    /// An element was missing a required attribute.
    MissingAttribute {
        /// The element that was missing an attribute.
        element: String,

        /// The attribute that expected to be present.
        attribute: &'static str,
    },

    /// An element had an attribute that isn't allowed.
    UnexpectedAttribute {
        /// The element that had the unexpected attribute.
        element: String,

        /// The unexpected attribute.
        attribute: String,

        /// The set of attributes allowed for this element.
        expected: &'static [&'static str],

        /// Whether the attribute appeared multiple times.
        is_duplicate: bool,
    },

    /// An element had a child element that wasn't allowed.
    UnexpectedElement {
        parent: String,
        element: String,
        expected: &'static [&'static str],
    },

    /// The document started with an element other than `<COLLADA>`.
    UnexpectedRootElement {
        element: String,
    },

    /// Generic error covering any case where less specific information is available.
    UnexpectedEvent(XmlEvent),

    /// The XML in the document was malformed in some way.
    XmlError(XmlError),
}

impl From<xml::reader::Error> for Error {
    fn from(from: xml::reader::Error) -> Error {
        Error {
            position: from.position(),
            kind: ErrorKind::XmlError(from),
        }
    }
}

/// A specialized result type for COLLADA parsing.
///
/// Specializes [`std::result::Result`][std::result::Result] to [`Error`][Error] for the purpose
/// of simplifying the signature of any falible COLLADA operation.
///
/// [std::result::Result]: https://doc.rust-lang.org/std/result/enum.Result.html
/// [Error]: struct.Error.html
pub type Result<T> = std::result::Result<T, Error>;

/// A URI in the COLLADA document.
///
/// Represents the [`xs:anyURI`][anyURI] XML data type.
///
/// [anyURI]: http://www.datypic.com/sc/xsd/t-xsd_anyURI.html
#[derive(Debug, Clone)]
pub struct AnyUri(String);
<|endoftext|>


<commit_before>use crate::command_prelude::*;

use cargo::ops::{self, CleanOptions};

pub fn cli() -> App {
    subcommand("clean")
        .about("Remove artifacts that cargo has generated in the past")
        .arg_package_spec_simple("Package to clean artifacts for")
        .arg_manifest_path()
        .arg_target_triple("Target triple to clean output for (default all)")
        .arg_target_dir()
        .arg_release("Whether or not to clean release artifacts")
        .arg_doc("Whether or not to clean just the documentation directory")
        .after_help(
            "\
If the --package argument is given, then SPEC is a package id specification
which indicates which package's artifacts should be cleaned out. If it is not
given, then all packages' artifacts are removed. For more information on SPEC
and its format, see the `cargo help pkgid` command.
",
        )
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    let ws = args.workspace(config)?;
    let opts = CleanOptions {
        config,
        spec: values(args, "package"),
        target: args.target(),
        release: args.is_present("release"),
        doc: args.is_present("doc"),
    };
    ops::clean(&ws, &opts)?;
    Ok(())
}
<commit_msg>Fix incorrect help message<commit_after>use crate::command_prelude::*;

use cargo::ops::{self, CleanOptions};

pub fn cli() -> App {
    subcommand("clean")
        .about("Remove artifacts that cargo has generated in the past")
        .arg_package_spec_simple("Package to clean artifacts for")
        .arg_manifest_path()
        .arg_target_triple("Target triple to clean output for")
        .arg_target_dir()
        .arg_release("Whether or not to clean release artifacts")
        .arg_doc("Whether or not to clean just the documentation directory")
        .after_help(
            "\
If the --package argument is given, then SPEC is a package id specification
which indicates which package's artifacts should be cleaned out. If it is not
given, then all packages' artifacts are removed. For more information on SPEC
and its format, see the `cargo help pkgid` command.
",
        )
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
    let ws = args.workspace(config)?;
    let opts = CleanOptions {
        config,
        spec: values(args, "package"),
        target: args.target(),
        release: args.is_present("release"),
        doc: args.is_present("doc"),
    };
    ops::clean(&ws, &opts)?;
    Ok(())
}
<|endoftext|>


<commit_before>use cairo::ImageSurface;
use cairo_sys;
use glib::translate::ToGlibPtr;
use wlroots::utils::current_time;
use wlroots::{project_box, Area, CompositorHandle, Origin, OutputHandle, OutputHandler,
              OutputLayoutHandle, Renderer, Size, SurfaceHandle, WL_SHM_FORMAT_ARGB8888};

use awesome::{Drawin, Objectable, DRAWINS_HANDLE, LUA};
use compositor::{Server, View};
use rlua::{self, AnyUserData, Lua};
use std::rc::Rc;

pub struct Output;

impl OutputHandler for Output {
    fn on_frame(&mut self, compositor: CompositorHandle, output: OutputHandle) {
        with_handles!([(compositor: {compositor}), (output: {output})] => {
            let state: &mut Server = compositor.data.downcast_mut().unwrap();
            let Server { ref mut layout,
                         ref mut views,
                         ref mut seat,
                         ref mut cursor,
                         .. } = *state;
            let renderer = compositor.renderer.as_mut().expect("gles2 disabled");
            let mut renderer = renderer.render(output, None);
            renderer.clear([0.25, 0.25, 0.25, 1.0]);
            render_views(&mut renderer, layout, views);
            LUA.with(|lua| {
                let lua = lua.borrow();
                match render_drawins(&*lua, &mut renderer) {
                    Ok(_) => {},
                    Err(err) => {
                        warn!("Error rendering drawins: {:#?}", err);
                    }
                }
            });
            let (lx, ly) = with_handles!([(cursor: {&cursor})] => {
                cursor.coords()
            }).unwrap();
            for drag_icon in &seat.drag_icons {
                with_handles!([(drag_icon: {&drag_icon.handle}), (layout: {&mut *layout})] => {
                    let (sx, sy) = drag_icon.position();
                    with_handles!([(surface: {&drag_icon.surface()})] => {
                        let (width, height) = surface.current_state().size();
                        let (render_width, render_height) =
                            (width * renderer.output.scale() as i32,
                            height * renderer.output.scale() as i32);
                        let render_box = Area::new(Origin::new((lx as i32) + sx, (ly as i32) + sy),
                        Size::new(render_width,
                                  render_height));

                        if layout.intersects(renderer.output, render_box) {
                            let transform = renderer.output.get_transform().invert();
                            let matrix = project_box(render_box,
                                                     transform,
                                                     0.0,
                                                     renderer.output
                                                     .transform_matrix());
                            renderer.render_texture_with_matrix(&surface.texture(), matrix);
                            surface.send_frame_done(current_time());
                        }

                        }).unwrap();
                    }).unwrap();
            }
        }).unwrap();
    }
}

/// Render all of the client views.
fn render_views(renderer: &mut Renderer,
                layout: &mut OutputLayoutHandle,
                views: &mut Vec<Rc<View>>) {
    for view in views.iter_mut().rev() {
        let origin = view.origin.get();
        view.for_each_surface(&mut |surface: SurfaceHandle, sx, sy| {
            with_handles!([(surface: {surface}), (layout: {&mut *layout})] => {
                let (width, height) = surface.current_state().size();
                let (render_width, render_height) =
                    (width * renderer.output.scale() as i32,
                    height * renderer.output.scale() as i32);
                let render_box = Area::new(Origin::new(origin.x + sx, origin.y + sy),
                                           Size::new(render_width,
                                                     render_height));

                if layout.intersects(renderer.output, render_box) {
                    let transform = renderer.output.get_transform().invert();
                    let matrix = project_box(render_box,
                                             transform,
                                             0.0,
                                             renderer.output
                                             .transform_matrix());
                    renderer.render_texture_with_matrix(&surface.texture(), matrix);
                    surface.send_frame_done(current_time());
                }
            }).expect("Could not render views")
        });
    }
}

/// Render all of the drawins provided by Lua.
fn render_drawins(lua: &Lua, renderer: &mut Renderer) -> rlua::Result<()> {
    let drawins = lua.named_registry_value::<Vec<AnyUserData>>(DRAWINS_HANDLE)?;
    for drawin in drawins {
        let mut drawin = Drawin::cast(drawin.into())?;
        if !drawin.get_visible()? {
            continue
        }
        let geometry = drawin.get_geometry()?;
        let drawable = drawin.drawable()?;
        let mut drawable_state = drawable.state()?;
        let surface = match drawable_state.surface.as_mut() {
            Some(surface) => surface,
            None => continue
        };
        let Area { size: Size { width, height },
                   .. } = geometry;
        let data = get_data(surface);
        let texture = renderer.create_texture_from_pixels(WL_SHM_FORMAT_ARGB8888,
                                                          (width * 4) as _,
                                                          width as _,
                                                          height as _,
                                                          data)
                              .expect("Could not allocate texture");
        let transform_matrix = renderer.output.transform_matrix();
        let inverted_transform = renderer.output.get_transform().invert();
        let matrix = project_box(geometry, inverted_transform, 0.0, transform_matrix);
        renderer.render_texture_with_matrix(&texture, matrix);
    }
    Ok(())
}

/// Get the data associated with an ImageSurface.
fn get_data(surface: &mut ImageSurface) -> &[u8] {
    use std::slice;
    // NOTE This is safe to do because there's one thread.
    //
    // We know Lua is not modifying it because it's not running.
    //
    // Otherwise we'd need to make a copy of the buffer. This ensure we
    // don't need to do that.
    unsafe {
        let len = surface.get_stride() as usize * surface.get_height() as usize;
        let surface = surface.to_glib_none().0;
        slice::from_raw_parts(cairo_sys::cairo_image_surface_get_data(surface as _), len)
    }
}
<commit_msg>render surface abstraction<commit_after>use cairo::ImageSurface;
use cairo_sys;
use glib::translate::ToGlibPtr;
use wlroots::utils::current_time;
use wlroots::{project_box, Area, CompositorHandle, Origin, OutputHandle, OutputHandler,
              OutputLayoutHandle, Renderer, Size, SurfaceHandle, WL_SHM_FORMAT_ARGB8888};

use awesome::{Drawin, Objectable, DRAWINS_HANDLE, LUA};
use compositor::{Server, View};
use rlua::{self, AnyUserData, Lua};
use std::rc::Rc;

pub struct Output;

impl OutputHandler for Output {
    fn on_frame(&mut self, compositor: CompositorHandle, output: OutputHandle) {
        with_handles!([(compositor: {compositor}), (output: {output})] => {
            let state: &mut Server = compositor.data.downcast_mut().unwrap();
            let Server { ref mut layout,
                         ref mut views,
                         ref mut seat,
                         ref mut cursor,
                         .. } = *state;
            let renderer = compositor.renderer.as_mut().expect("gles2 disabled");
            let mut renderer = renderer.render(output, None);
            renderer.clear([0.25, 0.25, 0.25, 1.0]);
            render_views(&mut renderer, layout, views);
            LUA.with(|lua| {
                let lua = lua.borrow();
                match render_drawins(&*lua, &mut renderer) {
                    Ok(_) => {},
                    Err(err) => {
                        warn!("Error rendering drawins: {:#?}", err);
                    }
                }
            });
            let (lx, ly) = with_handles!([(cursor: {&cursor})] => {
                cursor.coords()
            }).unwrap();
            for drag_icon in &seat.drag_icons {
                with_handles!([(drag_icon: {&drag_icon.handle})] => {
                    let (sx, sy) = drag_icon.position();
                    render_surface(&mut renderer, layout, &mut drag_icon.surface(), lx as i32 + sx, ly as i32 + sy);
                    }).unwrap();
            }
        }).unwrap();
    }
}

fn render_surface(renderer: &mut Renderer,
                  layout: &mut OutputLayoutHandle,
                  surface: &mut SurfaceHandle,
                  lx: i32,
                  ly: i32) {
    with_handles!([(surface: {surface}), (layout: {&mut *layout})] => {
        let (width, height) = surface.current_state().size();
        let (render_width, render_height) =
            (width * renderer.output.scale() as i32,
            height * renderer.output.scale() as i32);
        let render_box = Area::new(Origin::new(lx, ly),
        Size::new(render_width,
                  render_height));

        if layout.intersects(renderer.output, render_box) {
            let transform = renderer.output.get_transform().invert();
            let matrix = project_box(render_box,
                                     transform,
                                     0.0,
                                     renderer.output
                                     .transform_matrix());
            renderer.render_texture_with_matrix(&surface.texture(), matrix);
            surface.send_frame_done(current_time());
        }

    }).unwrap();
}

/// Render all of the client views.
fn render_views(renderer: &mut Renderer,
                layout: &mut OutputLayoutHandle,
                views: &mut Vec<Rc<View>>) {
    for view in views.iter_mut().rev() {
        let origin = view.origin.get();
        view.for_each_surface(&mut |mut surface: SurfaceHandle, sx, sy| {
            render_surface(renderer, layout, &mut surface, origin.x + sx, origin.y + sy);
        });
    }
}

/// Render all of the drawins provided by Lua.
fn render_drawins(lua: &Lua, renderer: &mut Renderer) -> rlua::Result<()> {
    let drawins = lua.named_registry_value::<Vec<AnyUserData>>(DRAWINS_HANDLE)?;
    for drawin in drawins {
        let mut drawin = Drawin::cast(drawin.into())?;
        if !drawin.get_visible()? {
            continue
        }
        let geometry = drawin.get_geometry()?;
        let drawable = drawin.drawable()?;
        let mut drawable_state = drawable.state()?;
        let surface = match drawable_state.surface.as_mut() {
            Some(surface) => surface,
            None => continue
        };
        let Area { size: Size { width, height },
                   .. } = geometry;
        let data = get_data(surface);
        let texture = renderer.create_texture_from_pixels(WL_SHM_FORMAT_ARGB8888,
                                                          (width * 4) as _,
                                                          width as _,
                                                          height as _,
                                                          data)
                              .expect("Could not allocate texture");
        let transform_matrix = renderer.output.transform_matrix();
        let inverted_transform = renderer.output.get_transform().invert();
        let matrix = project_box(geometry, inverted_transform, 0.0, transform_matrix);
        renderer.render_texture_with_matrix(&texture, matrix);
    }
    Ok(())
}

/// Get the data associated with an ImageSurface.
fn get_data(surface: &mut ImageSurface) -> &[u8] {
    use std::slice;
    // NOTE This is safe to do because there's one thread.
    //
    // We know Lua is not modifying it because it's not running.
    //
    // Otherwise we'd need to make a copy of the buffer. This ensure we
    // don't need to do that.
    unsafe {
        let len = surface.get_stride() as usize * surface.get_height() as usize;
        let surface = surface.to_glib_none().0;
        slice::from_raw_parts(cairo_sys::cairo_image_surface_get_data(surface as _), len)
    }
}
<|endoftext|>


<commit_before><commit_msg>Linux: Handle ENOBUFS by dynamically reducing packet size<commit_after><|endoftext|>


<commit_before>//
// sprocketnes/mem.rs
//
// Author: Patrick Walton
//

use input::Input;
use mapper::Mapper;
use ppu::{Oam, Ppu, Vram};
use util::debug_print;

//
// The memory interface
//

/// The basic memory interface
pub trait Mem {
    fn loadb(&mut self, addr: u16) -> u8;
    fn storeb(&mut self, addr: u16, val: u8);
}

pub trait MemUtil {
    fn loadw(&mut self, addr: u16) -> u16;
    fn storew(&mut self, addr: u16, val: u16);
    fn loadw_zp(&mut self, addr: u8) -> u16;
}

impl<M:Mem> MemUtil for M {
    fn loadw(&mut self, addr: u16) -> u16 {
        self.loadb(addr) as u16 | (self.loadb(addr + 1) as u16 << 8)
    }
    fn storew(&mut self, addr: u16, val: u16) {
        self.storeb(addr, (val & 0xff) as u8);
        self.storeb(addr + 1, ((val >> 8) & 0xff) as u8);
    }
    // Like loadw, but has wraparound behavior on the zero page for address 0xff.
    fn loadw_zp(&mut self, addr: u8) -> u16 {
        self.loadb(addr as u16) as u16 | (self.loadb((addr + 1) as u16) as u16 << 8)
    }
}

//
// The NES' paltry 2KB of RAM
//

pub struct Ram {
    data: [u8 * 0x800]
}

impl Mem for Ram {
    fn loadb(&mut self, addr: u16) -> u8     { self.data[addr & 0x7ff] }
    fn storeb(&mut self, addr: u16, val: u8) { self.data[addr & 0x7ff] = val }
}

//
// The main CPU memory map
//

pub struct MemMap {
    ram: Ram,
    ppu: Ppu<Vram,Oam>,
    input: Input,
    mapper: Mapper,
}

impl MemMap {
    static fn new(ppu: Ppu<Vram/&a,Oam>, input: Input, mapper: Mapper/&a) -> MemMap/&a {
        MemMap {
            ram: Ram { data: [ 0, ..0x800 ] },
            ppu: ppu,
            input: input,
            mapper: mapper
        }
    }
}

impl Mem for MemMap {
    fn loadb(&mut self, addr: u16) -> u8 {
        if addr < 0x2000 {
            self.ram.loadb(addr)
        } else if addr < 0x4000 {
            self.ppu.loadb(addr)
        } else if addr < 0x4018 {
            self.input.loadb(addr)
        } else {
            (self.mapper.loadb)(&mut self.mapper, addr)
        }
    }
    fn storeb(&mut self, addr: u16, val: u8) {
        if addr < 0x2000 {
            self.ram.storeb(addr, val)
        } else if addr < 0x4000 {
            self.ppu.storeb(addr, val)
        } else if addr < 0x4018 {
            self.input.storeb(addr, val)
        } else {
            (self.mapper.storeb)(&mut self.mapper, addr, val)
        }
    }
}

<commit_msg>Simplify the Ram definition a bit<commit_after>//
// sprocketnes/mem.rs
//
// Author: Patrick Walton
//

use input::Input;
use mapper::Mapper;
use ppu::{Oam, Ppu, Vram};
use util::debug_print;

//
// The memory interface
//

/// The basic memory interface
pub trait Mem {
    fn loadb(&mut self, addr: u16) -> u8;
    fn storeb(&mut self, addr: u16, val: u8);
}

pub trait MemUtil {
    fn loadw(&mut self, addr: u16) -> u16;
    fn storew(&mut self, addr: u16, val: u16);
    fn loadw_zp(&mut self, addr: u8) -> u16;
}

impl<M:Mem> MemUtil for M {
    fn loadw(&mut self, addr: u16) -> u16 {
        self.loadb(addr) as u16 | (self.loadb(addr + 1) as u16 << 8)
    }
    fn storew(&mut self, addr: u16, val: u16) {
        self.storeb(addr, (val & 0xff) as u8);
        self.storeb(addr + 1, ((val >> 8) & 0xff) as u8);
    }
    // Like loadw, but has wraparound behavior on the zero page for address 0xff.
    fn loadw_zp(&mut self, addr: u8) -> u16 {
        self.loadb(addr as u16) as u16 | (self.loadb((addr + 1) as u16) as u16 << 8)
    }
}

//
// The NES' paltry 2KB of RAM
//

pub struct Ram([u8 * 0x800]);
impl Mem for Ram {
    fn loadb(&mut self, addr: u16) -> u8     { self[addr & 0x7ff] }
    fn storeb(&mut self, addr: u16, val: u8) { self[addr & 0x7ff] = val }
}

//
// The main CPU memory map
//

pub struct MemMap {
    ram: Ram,
    ppu: Ppu<Vram,Oam>,
    input: Input,
    mapper: Mapper,
}

impl MemMap {
    static fn new(ppu: Ppu<Vram/&a,Oam>, input: Input, mapper: Mapper/&a) -> MemMap/&a {
        MemMap { ram: Ram([ 0, ..0x800 ]), ppu: ppu, input: input, mapper: mapper }
    }
}

impl Mem for MemMap {
    fn loadb(&mut self, addr: u16) -> u8 {
        if addr < 0x2000 {
            self.ram.loadb(addr)
        } else if addr < 0x4000 {
            self.ppu.loadb(addr)
        } else if addr < 0x4018 {
            self.input.loadb(addr)
        } else {
            (self.mapper.loadb)(&mut self.mapper, addr)
        }
    }
    fn storeb(&mut self, addr: u16, val: u8) {
        if addr < 0x2000 {
            self.ram.storeb(addr, val)
        } else if addr < 0x4000 {
            self.ppu.storeb(addr, val)
        } else if addr < 0x4018 {
            self.input.storeb(addr, val)
        } else {
            (self.mapper.storeb)(&mut self.mapper, addr, val)
        }
    }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate log4rs;

#[macro_use]
extern crate nickel;
use nickel::{Request, Response, MiddlewareResult, Nickel, MediaType};
use nickel::status::StatusCode;
use nickel::router::http_router::HttpRouter;

extern crate modifier;
use modifier::Modifier;

#[macro_use]
extern crate hyper;
use hyper::header::ContentLength;

use std::io::Read;

extern crate url;
use url::form_urlencoded;

extern crate webdriver;
use webdriver::response::{WebDriverResponse, ValueResponse};

use std::time;
use std::thread;

extern crate core;

extern crate rustc_serialize;

extern crate image;
use image::GenericImage;
use std::io::Cursor;

extern crate get_if_addrs;

extern crate plugin;
extern crate typemap;

mod chromedriver;
use chromedriver::{WebdriverRequestExtensions, WebdriverSession};

use core::ops::Deref;
extern crate rand;
extern crate regex;
#[macro_use] extern crate lazy_static;

header! { (XExtra, "X-Extra") => [String] }

impl<'a, D> Modifier<Response<'a, D>> for XExtra {
    fn modify(self, res: &mut Response<'a, D>) {
        res.headers_mut().set(self)
    }
}

fn xpath_helper(session: &WebdriverSession, xpath: String) -> Result<ValueResponse, String> {
    return match session.find_element_by_xpath(xpath) {
        Err(val) => Err(format!("Error while trying to get element: {:?}", val)),
        Ok(val) => {
            match val {
                WebDriverResponse::Generic(obj) => Ok(obj),
                _ => Err(format!("Didn't expect {:?}", val)),
            }
        }
    }
}

fn streams<'a, D>(request: &mut Request<D>, mut res: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let session = request.webdriver().deref().make_session();
    let mut buffer = String::new();
    request.origin.read_to_string(&mut buffer).unwrap();
    let mut parse = form_urlencoded::parse(buffer.as_bytes());
    let mut url = match parse.find(|k| k.0 == "url") {
        Some((_, value)) => value.into_owned(),
        None => return res.error(StatusCode::BadRequest, "No URL in request"),

    };
    let request_url = match url::Url::parse(&url) {
        Ok(value) => value,
        Err(_) => {
            return res.error(StatusCode::BadRequest,
                             format!("Request URL was dodgy: '{}'", url))
        }

    };
    let host = request_url.host_str().unwrap();
    let extra_fn: fn(WebdriverSession) -> String;
    let xpath = match host {
            "livestream.com" => {
                fn extra(session: WebdriverSession) -> String {
                    lazy_static! {
                        static ref RE: regex::Regex = regex::Regex::new(r"app-argument=http://livestream.com/accounts/(\d+)/events/(\d+)").unwrap();
                    }
                    let source = session.get_page_source();
                    let caps = RE.captures(&source).unwrap();
                    format!("[\"{}\",\"{}\"]", &caps[1], &caps[2])
                }
                extra_fn = extra;
                "//div[@id='image-container']/img"
            },
            "www.ustream.tv" => {
                fn extra(session: WebdriverSession) -> String {
                    let res = xpath_helper(&session, "//video[@id='UViewer']".to_string());
                    return match res {
                        Ok(val) => {
                            session.get_element_attribute(&val, "src")
                        }
                        Err(val) => {
                            warn!("Error while trying to get Ustream extra data {}", val);
                            "".to_string()
                        }
                    }
                }
                extra_fn = extra;
                "//video[@id='UViewer']"
            },
            "www.youtube.com" => {
                fn extra(_: WebdriverSession) -> String {
                    "".to_string()
                }
                extra_fn = extra;
                url = url + "?autoplay=1";
                "//div[@id='player']"
            }
            _ => {
                return res.error(StatusCode::BadRequest,
                                 format!("Request URL host ({}) wasn't in known list: '{}'",
                                         host,
                                         url))
            }

        }
        .to_string();

    session.goto_url(url);
    thread::sleep(time::Duration::from_secs(5));
    let element: ValueResponse = match session.find_element_by_xpath(xpath) {
        Err(val) => {
            return res.error(StatusCode::BadRequest,
                             format!("Error while trying to get element: {:?}", val))
        }
        Ok(val) => {
            match val {
                WebDriverResponse::Generic(obj) => obj,
                _ => return res.error(StatusCode::BadRequest, format!("Didn't expect {:?}", val)),
            }
        }
    };
    let element_location = {
        let loc = session.get_element_location(&element);
        loc.unwrap()
            .find("value")
            .expect("value")
            .clone()
    };
    let element_size = {
        let size = session.get_element_size(&element);
        size.unwrap()
            .find("value")
            .expect("value")
            .clone()
    };
    let screenshot = {
        let png = session.get_screenshot_as_png();
        png.unwrap()
    };

    let cursor = Cursor::new(&screenshot);
    let mut loaded_image = image::load(cursor, image::ImageFormat::PNG).unwrap();
    let (width, height) = loaded_image.dimensions();
    debug!("Loaded image dimensions: {} x {}", width, height);
    let cropped = loaded_image.crop(
		element_location.find("x").expect("x").as_u64().expect("numeric x") as u32,
		element_location.find("y").expect("y").as_u64().expect("numeric y") as u32,
		element_size.find("width").expect("width").as_u64().expect("numeric width") as u32,
		element_size.find("height").expect("height").as_u64().expect("numeric height") as u32);

    let mut output_buffer: Vec<u8> = Vec::new();
    cropped.save(&mut output_buffer, image::ImageFormat::PNG).unwrap();

    let extra = extra_fn(session);
    res.set(XExtra(extra));

    res.set(MediaType::Png);
    res.set(ContentLength(output_buffer.len() as u64));
    res.send(output_buffer)
}

fn run(ip: std::net::IpAddr, port: u16, client: chromedriver::Webdriver) {
    let mut server = Nickel::new();
    // app.set_debug(true);
    // app.set_log_level();
    server.utilize(chromedriver::WebdriverMiddleware::new(client));
    server.post("/streams", streams);
    server.listen((ip, port));
}

fn main() {
    log4rs::init_file("log.toml", Default::default()).unwrap();
    let port = 8000;
    let mut handles = Vec::new();
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        handles.push(::std::thread::spawn(move || {
            let client = chromedriver::Webdriver::new();
            let ip = iface.ip();
            info!("Listening on {}:{} for {}", ip, port, iface.name);
            run(ip, port, client);
        }));
    }

    info!("All listeners spawned");

    for handle in handles {
        handle.join().unwrap();
    }
}
<commit_msg>Make sure Ustream X-Extra is valid JSON<commit_after>#[macro_use]
extern crate log;
extern crate log4rs;

#[macro_use]
extern crate nickel;
use nickel::{Request, Response, MiddlewareResult, Nickel, MediaType};
use nickel::status::StatusCode;
use nickel::router::http_router::HttpRouter;

extern crate modifier;
use modifier::Modifier;

#[macro_use]
extern crate hyper;
use hyper::header::ContentLength;

use std::io::Read;

extern crate url;
use url::form_urlencoded;

extern crate webdriver;
use webdriver::response::{WebDriverResponse, ValueResponse};

use std::time;
use std::thread;

extern crate core;

extern crate rustc_serialize;

extern crate image;
use image::GenericImage;
use std::io::Cursor;

extern crate get_if_addrs;

extern crate plugin;
extern crate typemap;

mod chromedriver;
use chromedriver::{WebdriverRequestExtensions, WebdriverSession};

use core::ops::Deref;
extern crate rand;
extern crate regex;
#[macro_use] extern crate lazy_static;

header! { (XExtra, "X-Extra") => [String] }

impl<'a, D> Modifier<Response<'a, D>> for XExtra {
    fn modify(self, res: &mut Response<'a, D>) {
        res.headers_mut().set(self)
    }
}

fn xpath_helper(session: &WebdriverSession, xpath: String) -> Result<ValueResponse, String> {
    return match session.find_element_by_xpath(xpath) {
        Err(val) => Err(format!("Error while trying to get element: {:?}", val)),
        Ok(val) => {
            match val {
                WebDriverResponse::Generic(obj) => Ok(obj),
                _ => Err(format!("Didn't expect {:?}", val)),
            }
        }
    }
}

fn streams<'a, D>(request: &mut Request<D>, mut res: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let session = request.webdriver().deref().make_session();
    let mut buffer = String::new();
    request.origin.read_to_string(&mut buffer).unwrap();
    let mut parse = form_urlencoded::parse(buffer.as_bytes());
    let mut url = match parse.find(|k| k.0 == "url") {
        Some((_, value)) => value.into_owned(),
        None => return res.error(StatusCode::BadRequest, "No URL in request"),

    };
    let request_url = match url::Url::parse(&url) {
        Ok(value) => value,
        Err(_) => {
            return res.error(StatusCode::BadRequest,
                             format!("Request URL was dodgy: '{}'", url))
        }

    };
    let host = request_url.host_str().unwrap();
    let extra_fn: fn(WebdriverSession) -> String;
    let xpath = match host {
            "livestream.com" => {
                fn extra(session: WebdriverSession) -> String {
                    lazy_static! {
                        static ref RE: regex::Regex = regex::Regex::new(r"app-argument=http://livestream.com/accounts/(\d+)/events/(\d+)").unwrap();
                    }
                    let source = session.get_page_source();
                    let caps = RE.captures(&source).unwrap();
                    format!("[\"{}\",\"{}\"]", &caps[1], &caps[2])
                }
                extra_fn = extra;
                "//div[@id='image-container']/img"
            },
            "www.ustream.tv" => {
                fn extra(session: WebdriverSession) -> String {
                    let res = xpath_helper(&session, "//video[@id='UViewer']".to_string());
                    return match res {
                        Ok(val) => {
                            format!("\"{}\"", session.get_element_attribute(&val, "src"))
                        }
                        Err(val) => {
                            warn!("Error while trying to get Ustream extra data {}", val);
                            "".to_string()
                        }
                    }
                }
                extra_fn = extra;
                "//video[@id='UViewer']"
            },
            "www.youtube.com" => {
                fn extra(_: WebdriverSession) -> String {
                    "".to_string()
                }
                extra_fn = extra;
                url = url + "?autoplay=1";
                "//div[@id='player']"
            }
            _ => {
                return res.error(StatusCode::BadRequest,
                                 format!("Request URL host ({}) wasn't in known list: '{}'",
                                         host,
                                         url))
            }

        }
        .to_string();

    session.goto_url(url);
    thread::sleep(time::Duration::from_secs(5));
    let element: ValueResponse = match session.find_element_by_xpath(xpath) {
        Err(val) => {
            return res.error(StatusCode::BadRequest,
                             format!("Error while trying to get element: {:?}", val))
        }
        Ok(val) => {
            match val {
                WebDriverResponse::Generic(obj) => obj,
                _ => return res.error(StatusCode::BadRequest, format!("Didn't expect {:?}", val)),
            }
        }
    };
    let element_location = {
        let loc = session.get_element_location(&element);
        loc.unwrap()
            .find("value")
            .expect("value")
            .clone()
    };
    let element_size = {
        let size = session.get_element_size(&element);
        size.unwrap()
            .find("value")
            .expect("value")
            .clone()
    };
    let screenshot = {
        let png = session.get_screenshot_as_png();
        png.unwrap()
    };

    let cursor = Cursor::new(&screenshot);
    let mut loaded_image = image::load(cursor, image::ImageFormat::PNG).unwrap();
    let (width, height) = loaded_image.dimensions();
    debug!("Loaded image dimensions: {} x {}", width, height);
    let cropped = loaded_image.crop(
		element_location.find("x").expect("x").as_u64().expect("numeric x") as u32,
		element_location.find("y").expect("y").as_u64().expect("numeric y") as u32,
		element_size.find("width").expect("width").as_u64().expect("numeric width") as u32,
		element_size.find("height").expect("height").as_u64().expect("numeric height") as u32);

    let mut output_buffer: Vec<u8> = Vec::new();
    cropped.save(&mut output_buffer, image::ImageFormat::PNG).unwrap();

    let extra = extra_fn(session);
    res.set(XExtra(extra));

    res.set(MediaType::Png);
    res.set(ContentLength(output_buffer.len() as u64));
    res.send(output_buffer)
}

fn run(ip: std::net::IpAddr, port: u16, client: chromedriver::Webdriver) {
    let mut server = Nickel::new();
    // app.set_debug(true);
    // app.set_log_level();
    server.utilize(chromedriver::WebdriverMiddleware::new(client));
    server.post("/streams", streams);
    server.listen((ip, port));
}

fn main() {
    log4rs::init_file("log.toml", Default::default()).unwrap();
    let port = 8000;
    let mut handles = Vec::new();
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        handles.push(::std::thread::spawn(move || {
            let client = chromedriver::Webdriver::new();
            let ip = iface.ip();
            info!("Listening on {}:{} for {}", ip, port, iface.name);
            run(ip, port, client);
        }));
    }

    info!("All listeners spawned");

    for handle in handles {
        handle.join().unwrap();
    }
}
<|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/. */

use basedir::default_config_dir;
use opts;
use resource_files::resources_dir_path;
use rustc_serialize::json::{Json, ToJson};
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Write, stderr};
use std::path::PathBuf;
use std::sync::{Arc, RwLock};

lazy_static! {
    pub static ref PREFS: Preferences = {
        let prefs = read_prefs().ok().unwrap_or_else(HashMap::new);
        Preferences(Arc::new(RwLock::new(prefs)))
    };
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum PrefValue {
    Boolean(bool),
    String(String),
    Number(f64),
    Missing
}

impl PrefValue {
    pub fn from_json(data: Json) -> Result<PrefValue, ()> {
        let value = match data {
            Json::Boolean(x) => PrefValue::Boolean(x),
            Json::String(x) => PrefValue::String(x),
            Json::F64(x) => PrefValue::Number(x),
            Json::I64(x) => PrefValue::Number(x as f64),
            Json::U64(x) => PrefValue::Number(x as f64),
            _ => return Err(())
        };
        Ok(value)
    }

    pub fn as_boolean(&self) -> Option<bool> {
        match *self {
            PrefValue::Boolean(value) => {
                Some(value)
            },
            _ => None
        }
    }

    pub fn as_string(&self) -> Option<&str> {
        match *self {
            PrefValue::String(ref value) => {
                Some(&value)
            },
            _ => None
        }
    }

    pub fn as_i64(&self) -> Option<i64> {
        match *self {
            PrefValue::Number(x) => Some(x as i64),
            _ => None,
        }
    }

    pub fn as_u64(&self) -> Option<u64> {
        match *self {
            PrefValue::Number(x) => Some(x as u64),
            _ => None,
        }
    }
}

impl ToJson for PrefValue {
    fn to_json(&self) -> Json {
        match *self {
            PrefValue::Boolean(x) => {
                Json::Boolean(x)
            },
            PrefValue::String(ref x) => {
                Json::String(x.clone())
            },
            PrefValue::Number(x) => {
                Json::F64(x)
            },
            PrefValue::Missing => Json::Null
        }
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Pref {
    NoDefault(Arc<PrefValue>),
    WithDefault(Arc<PrefValue>, Option<Arc<PrefValue>>)
}

impl Pref {
    pub fn new(value: PrefValue) -> Pref {
        Pref::NoDefault(Arc::new(value))
    }

    fn new_default(value: PrefValue) -> Pref {
        Pref::WithDefault(Arc::new(value), None)
    }

    fn from_json(data: Json) -> Result<Pref, ()> {
        let value = try!(PrefValue::from_json(data));
        Ok(Pref::new_default(value))
    }

    pub fn value(&self) -> &Arc<PrefValue> {
        match *self {
            Pref::NoDefault(ref x) => x,
            Pref::WithDefault(ref default, ref override_value) => {
                match *override_value {
                    Some(ref x) => x,
                    None => default
                }
            }
        }
    }

    fn set(&mut self, value: PrefValue) {
        // TODO - this should error if we try to override a pref of one type
        // with a value of a different type
        match *self {
            Pref::NoDefault(ref mut pref_value) => {
                *pref_value = Arc::new(value)
            },
            Pref::WithDefault(_, ref mut override_value) => {
                *override_value = Some(Arc::new(value))
            }
        }
    }
}

impl ToJson for Pref {
    fn to_json(&self) -> Json {
        self.value().to_json()
    }
}

pub fn read_prefs_from_file<T>(mut file: T)
    -> Result<HashMap<String, Pref>, ()> where T: Read {
    let json = try!(Json::from_reader(&mut file).or_else(|e| {
        println!("Ignoring invalid JSON in preferences: {:?}.", e);
        Err(())
    }));

    let mut prefs = HashMap::new();
    if let Json::Object(obj) = json {
        for (name, value) in obj.into_iter() {
            match Pref::from_json(value) {
                Ok(x) => {
                    prefs.insert(name, x);
                },
                Err(_) => println!("Ignoring non-boolean/string/i64 preference value for {:?}", name),
            }
        }
    }
    Ok(prefs)
}

pub fn add_user_prefs() {
    match opts::get().config_dir {
        Some(ref config_path) => {
            let mut path = PathBuf::from(config_path);
            init_user_prefs(&mut path);
        }
        None => {
            let mut path = default_config_dir().unwrap();
            if path.join("prefs.json").exists() {
                init_user_prefs(&mut path);
            }
        }
    }
}

fn init_user_prefs(path: &mut PathBuf) {
    path.push("prefs.json");
    if let Ok(file) = File::open(path) {
        if let Ok(prefs) = read_prefs_from_file(file) {
            PREFS.extend(prefs);
        }
    } else {
    writeln!(&mut stderr(), "Error opening prefs.json from config directory")
        .expect("failed printing to stderr");
    }
}

fn read_prefs() -> Result<HashMap<String, Pref>, ()> {
    let mut path = try!(resources_dir_path().map_err(|_| ()));
    path.push("prefs.json");

    let file = try!(File::open(path).or_else(|e| {
        writeln!(&mut stderr(), "Error opening preferences: {:?}.", e)
            .expect("failed printing to stderr");
        Err(())
    }));

    read_prefs_from_file(file)
}

pub struct Preferences(Arc<RwLock<HashMap<String, Pref>>>);

impl Preferences {
    pub fn get(&self, name: &str) -> Arc<PrefValue> {
        self.0.read().unwrap().get(name).map_or(Arc::new(PrefValue::Missing), |x| x.value().clone())
    }

    pub fn cloned(&self) -> HashMap<String, Pref> {
        self.0.read().unwrap().clone()
    }

    pub fn is_mozbrowser_enabled(&self) -> bool {
        self.get("dom.mozbrowser.enabled").as_boolean().unwrap_or(false)
    }

    pub fn set(&self, name: &str, value: PrefValue) {
        let mut prefs = self.0.write().unwrap();
        if let Some(pref) = prefs.get_mut(name) {
            pref.set(value);
            return;
        }
        prefs.insert(name.to_owned(), Pref::new(value));
    }

    pub fn reset(&self, name: &str) -> Arc<PrefValue> {
        let mut prefs = self.0.write().unwrap();
        let result = match prefs.get_mut(name) {
            None => return Arc::new(PrefValue::Missing),
            Some(&mut Pref::NoDefault(_)) => Arc::new(PrefValue::Missing),
            Some(&mut Pref::WithDefault(ref default, ref mut set_value)) => {
                *set_value = None;
                default.clone()
            },
        };
        if *result == PrefValue::Missing {
            prefs.remove(name);
        }
        result
    }

    pub fn reset_all(&self) {
        let names = {
            self.0.read().unwrap().keys().cloned().collect::<Vec<String>>()
        };
        for name in names.iter() {
            self.reset(name);
        }
    }

    pub fn extend(&self, extension: HashMap<String, Pref>) {
        self.0.write().unwrap().extend(extension);
    }

    pub fn is_webvr_enabled(&self) -> bool {
        self.get("dom.webvr.enabled").as_boolean().unwrap_or(false)
    }
}
<commit_msg>Auto merge of #16818 - zombiefungus:fix15638, r=Manishearth<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/. */

use basedir::default_config_dir;
use num_cpus;
use opts;
use resource_files::resources_dir_path;
use rustc_serialize::json::{Json, ToJson};
use std::borrow::ToOwned;
use std::cmp::max;
use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Write, stderr};
use std::path::PathBuf;
use std::sync::{Arc, RwLock};

lazy_static! {
    pub static ref PREFS: Preferences = {
        let defaults = default_prefs();
        if let Ok(prefs) = read_prefs() {
            defaults.extend(prefs);
        }
        defaults
    };
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum PrefValue {
    Boolean(bool),
    String(String),
    Number(f64),
    Missing
}

impl PrefValue {
    pub fn from_json(data: Json) -> Result<PrefValue, ()> {
        let value = match data {
            Json::Boolean(x) => PrefValue::Boolean(x),
            Json::String(x) => PrefValue::String(x),
            Json::F64(x) => PrefValue::Number(x),
            Json::I64(x) => PrefValue::Number(x as f64),
            Json::U64(x) => PrefValue::Number(x as f64),
            _ => return Err(())
        };
        Ok(value)
    }

    pub fn as_boolean(&self) -> Option<bool> {
        match *self {
            PrefValue::Boolean(value) => {
                Some(value)
            },
            _ => None
        }
    }

    pub fn as_string(&self) -> Option<&str> {
        match *self {
            PrefValue::String(ref value) => {
                Some(&value)
            },
            _ => None
        }
    }

    pub fn as_i64(&self) -> Option<i64> {
        match *self {
            PrefValue::Number(x) => Some(x as i64),
            _ => None,
        }
    }

    pub fn as_u64(&self) -> Option<u64> {
        match *self {
            PrefValue::Number(x) => Some(x as u64),
            _ => None,
        }
    }
}

impl ToJson for PrefValue {
    fn to_json(&self) -> Json {
        match *self {
            PrefValue::Boolean(x) => {
                Json::Boolean(x)
            },
            PrefValue::String(ref x) => {
                Json::String(x.clone())
            },
            PrefValue::Number(x) => {
                Json::F64(x)
            },
            PrefValue::Missing => Json::Null
        }
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Pref {
    NoDefault(Arc<PrefValue>),
    WithDefault(Arc<PrefValue>, Option<Arc<PrefValue>>)
}

impl Pref {
    pub fn new(value: PrefValue) -> Pref {
        Pref::NoDefault(Arc::new(value))
    }

    fn new_default(value: PrefValue) -> Pref {
        Pref::WithDefault(Arc::new(value), None)
    }

    fn from_json(data: Json) -> Result<Pref, ()> {
        let value = try!(PrefValue::from_json(data));
        Ok(Pref::new_default(value))
    }

    pub fn value(&self) -> &Arc<PrefValue> {
        match *self {
            Pref::NoDefault(ref x) => x,
            Pref::WithDefault(ref default, ref override_value) => {
                match *override_value {
                    Some(ref x) => x,
                    None => default
                }
            }
        }
    }

    fn set(&mut self, value: PrefValue) {
        // TODO - this should error if we try to override a pref of one type
        // with a value of a different type
        match *self {
            Pref::NoDefault(ref mut pref_value) => {
                *pref_value = Arc::new(value)
            },
            Pref::WithDefault(_, ref mut override_value) => {
                *override_value = Some(Arc::new(value))
            }
        }
    }
}

impl ToJson for Pref {
    fn to_json(&self) -> Json {
        self.value().to_json()
    }
}

pub fn default_prefs() -> Preferences {
    let prefs = Preferences(Arc::new(RwLock::new(HashMap::new())));
    prefs.set("layout.threads", PrefValue::Number(
        max(num_cpus::get() * 3 / 4, 1) as f64));
    prefs
}

pub fn read_prefs_from_file<T>(mut file: T)
    -> Result<HashMap<String, Pref>, ()> where T: Read {
    let json = try!(Json::from_reader(&mut file).or_else(|e| {
        println!("Ignoring invalid JSON in preferences: {:?}.", e);
        Err(())
    }));

    let mut prefs = HashMap::new();
    if let Json::Object(obj) = json {
        for (name, value) in obj.into_iter() {
            match Pref::from_json(value) {
                Ok(x) => {
                    prefs.insert(name, x);
                },
                Err(_) => println!("Ignoring non-boolean/string/i64 preference value for {:?}", name),
            }
        }
    }
    Ok(prefs)
}

pub fn add_user_prefs() {
    match opts::get().config_dir {
        Some(ref config_path) => {
            let mut path = PathBuf::from(config_path);
            init_user_prefs(&mut path);
        }
        None => {
            let mut path = default_config_dir().unwrap();
            if path.join("prefs.json").exists() {
                init_user_prefs(&mut path);
            }
        }
    }
}

fn init_user_prefs(path: &mut PathBuf) {
    path.push("prefs.json");
    if let Ok(file) = File::open(path) {
        if let Ok(prefs) = read_prefs_from_file(file) {
            PREFS.extend(prefs);
        }
    } else {
    writeln!(&mut stderr(), "Error opening prefs.json from config directory")
        .expect("failed printing to stderr");
    }
}

fn read_prefs() -> Result<HashMap<String, Pref>, ()> {
    let mut path = try!(resources_dir_path().map_err(|_| ()));
    path.push("prefs.json");

    let file = try!(File::open(path).or_else(|e| {
        writeln!(&mut stderr(), "Error opening preferences: {:?}.", e)
            .expect("failed printing to stderr");
        Err(())
    }));

    read_prefs_from_file(file)
}

pub struct Preferences(Arc<RwLock<HashMap<String, Pref>>>);

impl Preferences {
    pub fn get(&self, name: &str) -> Arc<PrefValue> {
        self.0.read().unwrap().get(name).map_or(Arc::new(PrefValue::Missing), |x| x.value().clone())
    }

    pub fn cloned(&self) -> HashMap<String, Pref> {
        self.0.read().unwrap().clone()
    }

    pub fn is_mozbrowser_enabled(&self) -> bool {
        self.get("dom.mozbrowser.enabled").as_boolean().unwrap_or(false)
    }

    pub fn set(&self, name: &str, value: PrefValue) {
        let mut prefs = self.0.write().unwrap();
        if let Some(pref) = prefs.get_mut(name) {
            pref.set(value);
            return;
        }
        prefs.insert(name.to_owned(), Pref::new(value));
    }

    pub fn reset(&self, name: &str) -> Arc<PrefValue> {
        let mut prefs = self.0.write().unwrap();
        let result = match prefs.get_mut(name) {
            None => return Arc::new(PrefValue::Missing),
            Some(&mut Pref::NoDefault(_)) => Arc::new(PrefValue::Missing),
            Some(&mut Pref::WithDefault(ref default, ref mut set_value)) => {
                *set_value = None;
                default.clone()
            },
        };
        if *result == PrefValue::Missing {
            prefs.remove(name);
        }
        result
    }

    pub fn reset_all(&self) {
        let names = {
            self.0.read().unwrap().keys().cloned().collect::<Vec<String>>()
        };
        for name in names.iter() {
            self.reset(name);
        }
    }

    pub fn extend(&self, extension: HashMap<String, Pref>) {
        self.0.write().unwrap().extend(extension);
    }

    pub fn is_webvr_enabled(&self) -> bool {
        self.get("dom.webvr.enabled").as_boolean().unwrap_or(false)
    }
}
<|endoftext|>


<commit_before>// The &'static here means the return type has a static lifetime.
// This is a Rust feature that you don't need to worry about now.
pub fn hello() -> &'static str {
    "Goodbye, World!"
}
<commit_msg>🤖 Simplify `hello-world` stub (#1285)<commit_after>// &'static is a "lifetime specifier", something you'll learn more about later
pub fn hello() -> &'static str {
    "Goodbye, Mars!"
}
<|endoftext|>


<commit_before>extern crate algorithmia;
extern crate "rustc-serialize" as rustc_serialize;

use algorithmia::Service;
use algorithmia::algorithm::{AlgorithmOutput, Version};
use std::collections::HashMap;
use std::env;
use rustc_serialize::{json};

macro_rules! hashmap {
    ($( $key: expr => $val: expr ),*) => {{
         let mut map = ::std::collections::HashMap::new();
         $( map.insert($key, $val); )*
         map
    }}
}

// Map of all possible points to their possible destinations (with distances)
type SrcDestMap<'a> = HashMap<&'a str, HashMap<&'a str, u32>>;

// The input format for the kenny/Dijkstra algorithm
type DijkstraInput<'a> = (SrcDestMap<'a>, &'a str, &'a str);

// The output format for the kenny/Dijkstra algorithm
type Route = Vec<String>;

struct RouteMap<'a> {
  map: SrcDestMap<'a>
}

impl<'a> RouteMap<'a> {
    pub fn get_dijkstra_route(self, start: &'a str, end: &'a str) -> AlgorithmOutput<Route> {
        let api_key = match env::var("ALGORITHMIA_API_KEY") {
            Ok(key) => key,
            Err(e) => { panic!("ERROR: unable to get ALGORITHMIA_API_KEY: {}", e); }
        };
        let service = Service::new(&*api_key);
        let dijkstra = service.algorithm("anowell", "Dijkstra", Version::Latest);

        println!("Making request to: {}", dijkstra.algorithm.to_url());

        // Declaring type explicitly to enforce valid input types during build
        let input_data: DijkstraInput = (self.map, start, end);
        // println!("Input: {:?}", input_data);
        println!("Input:\n{}", json::as_pretty_json(&input_data));

        let output: AlgorithmOutput<Route> = match dijkstra.exec(&input_data) {
            Ok(out) => out,
            Err(why) => panic!("{:?}", why),
        };
        output
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let (start, end) = match &*args {
        [_, ref start, ref end] => (&**start, &**end),
        _ => ("a", "c"),
    };

    let input_map = RouteMap {
        map: hashmap!(
            "a" => hashmap!("b" => 1),
            "b" => hashmap!("a" => 2, "c" => 2),
            "c" => hashmap!("b" => 2, "d" => 1),
            "d" => hashmap!("a" => 1, "c" => 3)
        )
    };

    let output = input_map.get_dijkstra_route(start, end);
    println!("Shortest route: {:?}\nCompleted in {} seconds.", output.result, output.duration);
}
<commit_msg>Fix broken sample<commit_after>extern crate algorithmia;
extern crate rustc_serialize;

use algorithmia::Service;
use algorithmia::algorithm::{AlgorithmOutput, Version};
use std::collections::HashMap;
use std::env;
use rustc_serialize::{json};

macro_rules! hashmap {
    ($( $key: expr => $val: expr ),*) => {{
         let mut map = ::std::collections::HashMap::new();
         $( map.insert($key, $val); )*
         map
    }}
}

// Map of all possible points to their possible destinations (with distances)
type SrcDestMap<'a> = HashMap<&'a str, HashMap<&'a str, u32>>;

// The input format for the kenny/Dijkstra algorithm
type DijkstraInput<'a> = (SrcDestMap<'a>, &'a str, &'a str);

// The output format for the kenny/Dijkstra algorithm
type Route = Vec<String>;

struct RouteMap<'a> {
  map: SrcDestMap<'a>
}

impl<'a> RouteMap<'a> {
    pub fn get_dijkstra_route(self, start: &'a str, end: &'a str) -> AlgorithmOutput<Route> {
        let api_key = match env::var("ALGORITHMIA_API_KEY") {
            Ok(key) => key,
            Err(e) => { panic!("ERROR: unable to get ALGORITHMIA_API_KEY: {}", e); }
        };
        let service = Service::new(&*api_key);
        let dijkstra = service.algorithm("anowell", "Dijkstra", Version::Latest);

        println!("Making request to: {}", dijkstra.algorithm.to_url());

        // Declaring type explicitly to enforce valid input types during build
        let input_data: DijkstraInput = (self.map, start, end);
        // println!("Input: {:?}", input_data);
        println!("Input:\n{}", json::as_pretty_json(&input_data));

        let output: AlgorithmOutput<Route> = match dijkstra.exec(&input_data) {
            Ok(out) => out,
            Err(why) => panic!("{:?}", why),
        };
        output
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let (start, end) = match &*args {
        [_, ref start, ref end] => (&**start, &**end),
        _ => ("a", "c"),
    };

    let input_map = RouteMap {
        map: hashmap!(
            "a" => hashmap!("b" => 1),
            "b" => hashmap!("a" => 2, "c" => 2),
            "c" => hashmap!("b" => 2, "d" => 1),
            "d" => hashmap!("a" => 1, "c" => 3)
        )
    };

    let output = input_map.get_dijkstra_route(start, end);
    println!("Shortest route: {:?}\nCompleted in {} seconds.", output.result, output.duration);
}
<|endoftext|>